NotePlayHandle, InstrumentFunctions: reworked stacking and arpeggio handling

Instead of having various flags for realizing the arpeggion functionality
use a more generic approach here using the recently introduced "origin"
property.
This commit is contained in:
Tobias Doerffel
2014-03-26 11:39:14 +01:00
parent 89dc820d7b
commit 6650dd356d
5 changed files with 74 additions and 139 deletions

View File

@@ -51,6 +51,8 @@ public:
{
OriginPattern, /*! playback of a note from a pattern */
OriginMidiInput, /*! playback of a MIDI note input event */
OriginNoteStacking, /*! created by note stacking instrument function */
OriginArpeggio, /*! created by arpeggio instrument function */
OriginCount
};
typedef Origins Origin;
@@ -60,7 +62,6 @@ public:
const f_cnt_t frames,
const note& noteToPlay,
NotePlayHandle* parent = NULL,
const bool isPartOfArp = false,
int midiEventChannel = -1,
Origin origin = OriginPattern );
virtual ~NotePlayHandle();
@@ -160,27 +161,24 @@ public:
return m_instrumentTrack;
}
/*! Returns whether note is a top note, e.g. is not part of an arpeggio or a chord */
bool isTopNote() const
/*! Returns whether note has a parent, e.g. is not part of an arpeggio or a chord */
bool hasParent() const
{
return m_topNote;
return m_hasParent;
}
/*! Returns whether note is part of an arpeggio playback */
bool isPartOfArpeggio() const
/*! Returns origin of note */
Origin origin() const
{
return m_partOfArpeggio;
return m_origin;
}
/*! Sets whether note is part of an arpeggio playback */
void setPartOfArpeggio( const bool _on )
/*! Returns whether note has children */
bool isMasterNote() const
{
m_partOfArpeggio = _on;
return m_subNotes.size() > 0 || m_hadChildren;
}
/*! Returns whether note is base note for arpeggio */
bool isArpeggioBaseNote() const;
/*! Returns whether note is muted */
bool isMuted() const
{
@@ -268,11 +266,8 @@ private:
// release of note
NotePlayHandleList m_subNotes; // used for chords and arpeggios
volatile bool m_released; // indicates whether note is released
bool m_topNote; // indicates whether note is a
// base-note (i.e. no sub-note)
bool m_partOfArpeggio; // indicates whether note is part of
// an arpeggio (either base-note or
// sub-note)
bool m_hasParent;
bool m_hadChildren;
bool m_muted; // indicates whether note is muted
track* m_bbTrack; // related BB track

View File

@@ -713,7 +713,7 @@ void lb302Synth::playNote( NotePlayHandle * _n, sampleFrame * _working_buffer )
{
//fpp_t framesPerPeriod = engine::mixer()->framesPerPeriod();
if( _n->isArpeggioBaseNote() )
if( _n->isMasterNote() )
{
return;
}

View File

@@ -231,30 +231,24 @@ void InstrumentFunctionNoteStacking::processNote( NotePlayHandle * _n )
// at the same time we only add sub-notes if nothing of the note was
// played yet, because otherwise we would add chord-subnotes every
// time an audio-buffer is rendered...
if( ( ( _n->isTopNote() && _n->instrumentTrack()->isArpeggioEnabled() == false ) || _n->isPartOfArpeggio() ) &&
_n->totalFramesPlayed() == 0 &&
m_chordsEnabledModel.value() == true )
if( ( _n->origin() == NotePlayHandle::OriginArpeggio || ( _n->hasParent() == false && _n->instrumentTrack()->isArpeggioEnabled() == false ) ) &&
_n->totalFramesPlayed() == 0 &&
m_chordsEnabledModel.value() == true )
{
// then insert sub-notes for chord
const int selected_chord = m_chordsModel.value();
for( int octave_cnt = 0;
octave_cnt < m_chordRangeModel.value(); ++octave_cnt )
for( int octave_cnt = 0; octave_cnt < m_chordRangeModel.value(); ++octave_cnt )
{
const int sub_note_key_base = base_note_key +
octave_cnt * KeysPerOctave;
const int sub_note_key_base = base_note_key + octave_cnt * KeysPerOctave;
// if octave_cnt == 1 we're in the first octave and
// the base-note is already done, so we don't have to
// create it in the following loop, then we loop until
// there's a -1 in the interval-array
for( int i = ( octave_cnt == 0 ) ? 1 : 0;
i < chord_table[selected_chord].size();
++i )
for( int i = ( octave_cnt == 0 ) ? 1 : 0; i < chord_table[selected_chord].size(); ++i )
{
// add interval to sub-note-key
const int sub_note_key = sub_note_key_base +
(int) chord_table[
selected_chord][i];
const int sub_note_key = sub_note_key_base + (int) chord_table[selected_chord][i];
// maybe we're out of range -> let's get outta
// here!
if( sub_note_key > NumKeys )
@@ -262,16 +256,12 @@ void InstrumentFunctionNoteStacking::processNote( NotePlayHandle * _n )
break;
}
// create copy of base-note
note note_copy( _n->length(), 0, sub_note_key,
_n->getVolume(),
_n->getPanning(),
_n->detuning() );
note note_copy( _n->length(), 0, sub_note_key, _n->getVolume(), _n->getPanning(), _n->detuning() );
// create sub-note-play-handle, only note is
// different
new NotePlayHandle( _n->instrumentTrack(),
_n->offset(),
_n->frames(), note_copy,
_n );
new NotePlayHandle( _n->instrumentTrack(), _n->offset(), _n->frames(), note_copy,
_n, -1, NotePlayHandle::OriginNoteStacking );
}
}
}
@@ -310,10 +300,8 @@ InstrumentFunctionArpeggio::InstrumentFunctionArpeggio( Model * _parent ) :
m_arpEnabledModel( false ),
m_arpModel( this, tr( "Arpeggio type" ) ),
m_arpRangeModel( 1.0f, 1.0f, 9.0f, 1.0f, this, tr( "Arpeggio range" ) ),
m_arpTimeModel( 100.0f, 25.0f, 2000.0f, 1.0f, 2000, this,
tr( "Arpeggio time" ) ),
m_arpGateModel( 100.0f, 1.0f, 200.0f, 1.0f, this,
tr( "Arpeggio gate" ) ),
m_arpTimeModel( 100.0f, 25.0f, 2000.0f, 1.0f, 2000, this, tr( "Arpeggio time" ) ),
m_arpGateModel( 100.0f, 1.0f, 200.0f, 1.0f, this, tr( "Arpeggio gate" ) ),
m_arpDirectionModel( this, tr( "Arpeggio direction" ) ),
m_arpModeModel( this, tr( "Arpeggio mode" ) )
{
@@ -325,10 +313,8 @@ InstrumentFunctionArpeggio::InstrumentFunctionArpeggio( Model * _parent ) :
m_arpDirectionModel.addItem( tr( "Up" ), new PixmapLoader( "arp_up" ) );
m_arpDirectionModel.addItem( tr( "Down" ), new PixmapLoader( "arp_down" ) );
m_arpDirectionModel.addItem( tr( "Up and down" ),
new PixmapLoader( "arp_up_and_down" ) );
m_arpDirectionModel.addItem( tr( "Random" ),
new PixmapLoader( "arp_random" ) );
m_arpDirectionModel.addItem( tr( "Up and down" ), new PixmapLoader( "arp_up_and_down" ) );
m_arpDirectionModel.addItem( tr( "Random" ), new PixmapLoader( "arp_random" ) );
m_arpDirectionModel.setInitValue( ArpDirUp );
m_arpModeModel.addItem( tr( "Free" ), new PixmapLoader( "arp_free" ) );
@@ -349,9 +335,10 @@ InstrumentFunctionArpeggio::~InstrumentFunctionArpeggio()
void InstrumentFunctionArpeggio::processNote( NotePlayHandle * _n )
{
const int base_note_key = _n->key();
if( _n->isTopNote() == false ||
!m_arpEnabledModel.value() ||
( _n->isReleased() && _n->releaseFramesDone() >= _n->actualReleaseFramesToDo() ) )
if( _n->origin() == NotePlayHandle::OriginArpeggio ||
_n->origin() == NotePlayHandle::OriginNoteStacking ||
!m_arpEnabledModel.value() ||
( _n->isReleased() && _n->releaseFramesDone() >= _n->actualReleaseFramesToDo() ) )
{
return;
}
@@ -359,8 +346,8 @@ void InstrumentFunctionArpeggio::processNote( NotePlayHandle * _n )
const int selected_arp = m_arpModel.value();
ConstNotePlayHandleList cnphv = NotePlayHandle::nphsOfInstrumentTrack(
_n->instrumentTrack() );
ConstNotePlayHandleList cnphv = NotePlayHandle::nphsOfInstrumentTrack( _n->instrumentTrack() );
if( m_arpModeModel.value() != FreeMode && cnphv.size() == 0 )
{
// maybe we're playing only a preset-preview-note?
@@ -379,27 +366,23 @@ void InstrumentFunctionArpeggio::processNote( NotePlayHandle * _n )
const int total_range = range * cnphv.size();
// number of frames that every note should be played
const f_cnt_t arp_frames = (f_cnt_t)( m_arpTimeModel.value() / 1000.0f *
engine::mixer()->processingSampleRate() );
const f_cnt_t gated_frames = (f_cnt_t)( m_arpGateModel.value() *
arp_frames / 100.0f );
const f_cnt_t arp_frames = (f_cnt_t)( m_arpTimeModel.value() / 1000.0f * engine::mixer()->processingSampleRate() );
const f_cnt_t gated_frames = (f_cnt_t)( m_arpGateModel.value() * arp_frames / 100.0f );
// used for calculating remaining frames for arp-note, we have to add
// arp_frames-1, otherwise the first arp-note will not be setup
// correctly... -> arp_frames frames silence at the start of every note!
int cur_frame = ( ( m_arpModeModel.value() != FreeMode ) ?
cnphv.first()->totalFramesPlayed() :
_n->totalFramesPlayed() ) + arp_frames - 1;
cnphv.first()->totalFramesPlayed() :
_n->totalFramesPlayed() ) + arp_frames - 1;
// used for loop
f_cnt_t frames_processed = 0;
while( frames_processed < engine::mixer()->framesPerPeriod() )
{
const f_cnt_t remaining_frames_for_cur_arp = arp_frames -
( cur_frame % arp_frames );
const f_cnt_t remaining_frames_for_cur_arp = arp_frames - ( cur_frame % arp_frames );
// does current arp-note fill whole audio-buffer?
if( remaining_frames_for_cur_arp >
engine::mixer()->framesPerPeriod() )
if( remaining_frames_for_cur_arp > engine::mixer()->framesPerPeriod() )
{
// then we don't have to do something!
break;
@@ -413,8 +396,7 @@ void InstrumentFunctionArpeggio::processNote( NotePlayHandle * _n )
// in sorted mode: is it our turn or do we have to be quiet for
// now?
if( m_arpModeModel.value() == SortMode &&
( ( cur_frame / arp_frames ) % total_range ) /
range != (f_cnt_t) _n->index() )
( ( cur_frame / arp_frames ) % total_range ) / range != (f_cnt_t) _n->index() )
{
// update counters
frames_processed += arp_frames;
@@ -439,33 +421,28 @@ void InstrumentFunctionArpeggio::processNote( NotePlayHandle * _n )
// once down -> makes 2 * range possible notes...
// because we don't play the lower and upper notes
// twice, we have to subtract 2
cur_arp_idx = ( cur_frame / arp_frames ) %
( range * 2 - 2 );
cur_arp_idx = ( cur_frame / arp_frames ) % ( range * 2 - 2 );
// if greater than range, we have to play down...
// looks like the code for arp_dir==DOWN... :)
if( cur_arp_idx >= range )
{
cur_arp_idx = range - cur_arp_idx %
( range - 1 ) - 1;
cur_arp_idx = range - cur_arp_idx % ( range - 1 ) - 1;
}
}
else if( dir == ArpDirRandom )
{
// just pick a random chord-index
cur_arp_idx = (int)( range * ( (float) rand() /
(float) RAND_MAX ) );
cur_arp_idx = (int)( range * ( (float) rand() / (float) RAND_MAX ) );
}
// now calculate final key for our arp-note
const int sub_note_key = base_note_key + (cur_arp_idx /
cur_chord_size ) *
KeysPerOctave +
chord_table[selected_arp][cur_arp_idx % cur_chord_size];
const int sub_note_key = base_note_key + (cur_arp_idx / cur_chord_size ) *
KeysPerOctave + chord_table[selected_arp][cur_arp_idx % cur_chord_size];
// range-checking
if( sub_note_key >= NumKeys ||
sub_note_key < 0 ||
engine::mixer()->criticalXRuns() )
engine::mixer()->criticalXRuns() )
{
continue;
}
@@ -477,34 +454,20 @@ void InstrumentFunctionArpeggio::processNote( NotePlayHandle * _n )
}
// create new arp-note
note new_note( MidiTime( 0 ), MidiTime( 0 ),
sub_note_key,
(volume_t)
qRound( _n->getVolume() * vol_level ),
_n->getPanning(), _n->detuning() );
// create sub-note-play-handle, only ptr to note is different
// and is_arp_note=true
new NotePlayHandle( _n->instrumentTrack(),
( ( m_arpModeModel.value() != FreeMode ) ?
cnphv.first()->offset() :
_n->offset() ) +
frames_processed,
gated_frames,
new_note,
_n, true );
( ( m_arpModeModel.value() != FreeMode ) ? cnphv.first()->offset() : _n->offset() ) + frames_processed,
gated_frames,
note( MidiTime( 0 ), MidiTime( 0 ), sub_note_key, (volume_t) qRound( _n->getVolume() * vol_level ),
_n->getPanning(), _n->detuning() ),
_n, -1, NotePlayHandle::OriginArpeggio );
// update counters
frames_processed += arp_frames;
cur_frame += arp_frames;
}
// make sure, note is handled as arp-base-note, even if we didn't add a
// sub-note so far
if( m_arpModeModel.value() != FreeMode )
{
_n->setPartOfArpeggio( true );
}
}

View File

@@ -49,7 +49,6 @@ NotePlayHandle::NotePlayHandle( InstrumentTrack* instrumentTrack,
const f_cnt_t _frames,
const note& n,
NotePlayHandle *parent,
const bool _part_of_arp,
int midiEventChannel,
Origin origin ) :
PlayHandle( TypeNotePlayHandle, _offset ),
@@ -63,8 +62,8 @@ NotePlayHandle::NotePlayHandle( InstrumentTrack* instrumentTrack,
m_releaseFramesToDo( 0 ),
m_releaseFramesDone( 0 ),
m_released( false ),
m_topNote( parent == NULL ),
m_partOfArpeggio( _part_of_arp ),
m_hasParent( parent != NULL ),
m_hadChildren( false ),
m_muted( false ),
m_bbTrack( NULL ),
m_origTempo( engine::getSong()->getTempo() ),
@@ -76,7 +75,7 @@ NotePlayHandle::NotePlayHandle( InstrumentTrack* instrumentTrack,
m_midiChannel( midiEventChannel >= 0 ? midiEventChannel : instrumentTrack->midiPort()->realOutputChannel() ),
m_origin( origin )
{
if( isTopNote() )
if( hasParent() == false )
{
m_baseDetuning = new BaseDetuning( detuning() );
m_instrumentTrack->m_processHandles.push_back( this );
@@ -86,10 +85,7 @@ NotePlayHandle::NotePlayHandle( InstrumentTrack* instrumentTrack,
m_baseDetuning = parent->m_baseDetuning;
parent->m_subNotes.push_back( this );
// if there was an arp-note added and parent is a base-note
// we set arp-note-flag for indicating that parent is an
// arpeggio-base-note
parent->m_partOfArpeggio = isPartOfArpeggio() && parent->isTopNote();
parent->m_hadChildren = true;
m_bbTrack = parent->m_bbTrack;
}
@@ -104,7 +100,7 @@ NotePlayHandle::NotePlayHandle( InstrumentTrack* instrumentTrack,
m_instrumentTrack->midiNoteOn( *this );
}
if( !isTopNote() || !instrumentTrack->isArpeggioEnabled() )
if( !isMasterNote() || !instrumentTrack->isArpeggioEnabled() )
{
const int baseVelocity = m_instrumentTrack->midiPort()->baseVelocity();
@@ -122,7 +118,7 @@ NotePlayHandle::~NotePlayHandle()
{
noteOff( 0 );
if( isTopNote() )
if( hasParent() == false )
{
delete m_baseDetuning;
m_instrumentTrack->m_processHandles.removeAll( this );
@@ -209,12 +205,13 @@ void NotePlayHandle::play( sampleFrame * _working_buffer )
if( m_released )
{
f_cnt_t todo = engine::mixer()->framesPerPeriod();
// if this note is base-note for arpeggio, always set
// m_releaseFramesToDo to bigger value than m_releaseFramesDone
// because we do not allow NotePlayHandle::isFinished() to be true
// until all sub-notes are completely played and no new ones
// are inserted by arpAndChordsTabWidget::processNote()
if( isArpeggioBaseNote() )
if( isMasterNote() )
{
m_releaseFramesToDo = m_releaseFramesDone + 2 * engine::mixer()->framesPerPeriod();
}
@@ -274,16 +271,6 @@ void NotePlayHandle::play( sampleFrame * _working_buffer )
}
}
// if this note is a base-note and there're no more sub-notes left we
// can set m_releaseFramesDone to m_releaseFramesToDo so that
// NotePlayHandle::isFinished() returns true and also this base-note is
// removed from mixer's active note vector
if( m_released && isArpeggioBaseNote() && m_subNotes.size() == 0 )
{
m_releaseFramesDone = m_releaseFramesToDo;
m_frames = 0;
}
// update internal data
m_totalFramesPlayed += engine::mixer()->framesPerPeriod();
}
@@ -344,7 +331,7 @@ void NotePlayHandle::noteOff( const f_cnt_t _s )
m_framesBeforeRelease = _s;
m_releaseFramesToDo = qMax<f_cnt_t>( 0, m_instrumentTrack->m_soundShaping.releaseFrames() );
if( !isTopNote() || !instrumentTrack()->isArpeggioEnabled() )
if( hasParent() || !instrumentTrack()->isArpeggioEnabled() )
{
// send MidiNoteOff event
m_instrumentTrack->processOutEvent(
@@ -367,8 +354,7 @@ void NotePlayHandle::noteOff( const f_cnt_t _s )
f_cnt_t NotePlayHandle::actualReleaseFramesToDo() const
{
return m_instrumentTrack->m_soundShaping.releaseFrames(/*
isArpeggioBaseNote()*/ );
return m_instrumentTrack->m_soundShaping.releaseFrames();
}
@@ -395,19 +381,10 @@ float NotePlayHandle::volumeLevel( const f_cnt_t _frame )
bool NotePlayHandle::isArpeggioBaseNote() const
{
return isTopNote() && ( m_partOfArpeggio || m_instrumentTrack->isArpeggioEnabled() );
}
void NotePlayHandle::mute()
{
// mute all sub-notes
for( NotePlayHandleList::Iterator it = m_subNotes.begin();
it != m_subNotes.end(); ++it )
for( NotePlayHandleList::Iterator it = m_subNotes.begin(); it != m_subNotes.end(); ++it )
{
( *it )->mute();
}
@@ -471,10 +448,11 @@ bool NotePlayHandle::operator==( const NotePlayHandle & _nph ) const
offset() == _nph.offset() &&
m_totalFramesPlayed == _nph.m_totalFramesPlayed &&
m_released == _nph.m_released &&
m_topNote == _nph.m_topNote &&
m_partOfArpeggio == _nph.m_partOfArpeggio &&
m_hasParent == _nph.m_hasParent &&
m_origBaseNote == _nph.m_origBaseNote &&
m_muted == _nph.m_muted;
m_muted == _nph.m_muted &&
m_midiChannel == _nph.m_midiChannel &&
m_origin == _nph.m_origin;
}

View File

@@ -252,7 +252,7 @@ void InstrumentTrack::processInEvent( const MidiEvent& event, const MidiTime& ti
NotePlayHandle* nph = new NotePlayHandle( this, time.frames( engine::framesPerTick() ),
typeInfo<f_cnt_t>::max() / 2,
note( MidiTime(), MidiTime(), event.key(), event.volume( midiPort()->baseVelocity() ) ),
NULL, false, event.channel(),
NULL, event.channel(),
NotePlayHandle::OriginMidiInput );
if( engine::mixer()->addPlayHandle( nph ) )
{
@@ -431,18 +431,17 @@ f_cnt_t InstrumentTrack::beatLen( NotePlayHandle * _n ) const
void InstrumentTrack::playNote( NotePlayHandle * _n,
sampleFrame * _working_buffer )
void InstrumentTrack::playNote( NotePlayHandle* n, sampleFrame* workingBuffer )
{
// arpeggio- and chord-widget has to do its work -> adding sub-notes
// for chords/arpeggios
m_noteStacking.processNote( _n );
m_arpeggio.processNote( _n );
m_noteStacking.processNote( n );
m_arpeggio.processNote( n );
if( !_n->isArpeggioBaseNote() && m_instrument != NULL )
if( n->isMasterNote() == false && m_instrument != NULL )
{
// all is done, so now lets play the note!
m_instrument->playNote( _n, _working_buffer );
m_instrument->playNote( n, workingBuffer );
}
}