NotePlayHandle: send correct MidiNoteOff events if base note changed
When changing an InstrumentTrack's base note while NotePlayHandles are
active, they will send a wrong MidiNoteOff event due to the masterKey()
translation in InstrumentTrack::processOutEvent().
Therefore in NotePlayHandle remember the original base note value and
add the difference between original and current base note to the value
returned by NotePlayHandle::key(). Fixes hanging notes in MIDI-based
instruments such as ZynAddSubFX.
Furthermore some coding style improvements.
Closes #3146975.
(cherry picked from commit a9717c0cc3)
This commit is contained in:
@@ -88,12 +88,12 @@ public:
|
||||
void playNote( notePlayHandle * _n, sampleFrame * _working_buffer );
|
||||
|
||||
QString instrumentName() const;
|
||||
inline const Instrument * instrument() const
|
||||
const Instrument *instrument() const
|
||||
{
|
||||
return m_instrument;
|
||||
}
|
||||
|
||||
inline Instrument * instrument()
|
||||
Instrument *instrument()
|
||||
{
|
||||
return m_instrument;
|
||||
}
|
||||
@@ -108,7 +108,7 @@ public:
|
||||
int masterKey( int _midi_key ) const;
|
||||
|
||||
// translate pitch to midi-pitch [0,16383]
|
||||
inline int midiPitch() const
|
||||
int midiPitch() const
|
||||
{
|
||||
return (int)( ( m_pitchModel.value()+100 ) * 16383 ) / 200;
|
||||
}
|
||||
@@ -135,17 +135,17 @@ public:
|
||||
// load instrument whose name matches given one
|
||||
Instrument * loadInstrument( const QString & _instrument_name );
|
||||
|
||||
inline AudioPort * audioPort()
|
||||
AudioPort * audioPort()
|
||||
{
|
||||
return &m_audioPort;
|
||||
}
|
||||
|
||||
inline MidiPort * midiPort()
|
||||
MidiPort * midiPort()
|
||||
{
|
||||
return &m_midiPort;
|
||||
}
|
||||
|
||||
Piano * pianoModel()
|
||||
Piano *pianoModel()
|
||||
{
|
||||
return &m_piano;
|
||||
}
|
||||
|
||||
@@ -56,8 +56,9 @@ public:
|
||||
virtual ~notePlayHandle();
|
||||
|
||||
virtual void setVolume( const volume_t _volume = DefaultVolume );
|
||||
|
||||
int getMidiVelocity() const;
|
||||
|
||||
int midiVelocity() const;
|
||||
int midiKey() const;
|
||||
|
||||
const float & frequency() const
|
||||
{
|
||||
@@ -84,7 +85,7 @@ public:
|
||||
inline fpp_t framesLeftForCurrentPeriod() const
|
||||
{
|
||||
return (fpp_t) qMin<f_cnt_t>( framesLeft(),
|
||||
engine::getMixer()->framesPerPeriod() );
|
||||
engine::getMixer()->framesPerPeriod() );
|
||||
}
|
||||
|
||||
|
||||
@@ -130,16 +131,21 @@ public:
|
||||
float volumeLevel( const f_cnt_t _frame );
|
||||
|
||||
// returns instrument-track this note-play-handle plays
|
||||
inline InstrumentTrack * instrumentTrack()
|
||||
const InstrumentTrack *instrumentTrack() const
|
||||
{
|
||||
return m_instrumentTrack;
|
||||
}
|
||||
|
||||
// returns whether note is a base-note, e.g. is not part of an arpeggio
|
||||
// or a chord
|
||||
inline bool isBaseNote() const
|
||||
InstrumentTrack *instrumentTrack()
|
||||
{
|
||||
return m_baseNote;
|
||||
return m_instrumentTrack;
|
||||
}
|
||||
|
||||
// returns whether note is a top note, e.g. is not part of an arpeggio
|
||||
// or a chord
|
||||
inline bool isTopNote() const
|
||||
{
|
||||
return m_topNote;
|
||||
}
|
||||
|
||||
inline bool isPartOfArpeggio() const
|
||||
@@ -200,14 +206,14 @@ public:
|
||||
|
||||
|
||||
private:
|
||||
class baseDetuning
|
||||
class BaseDetuning
|
||||
{
|
||||
public:
|
||||
baseDetuning( DetuningHelper * _detuning );
|
||||
BaseDetuning( DetuningHelper *detuning );
|
||||
|
||||
void setValue( float _val )
|
||||
void setValue( float val )
|
||||
{
|
||||
m_value = _val;
|
||||
m_value = val;
|
||||
}
|
||||
|
||||
float value() const
|
||||
@@ -224,38 +230,40 @@ private:
|
||||
|
||||
|
||||
InstrumentTrack * m_instrumentTrack; // needed for calling
|
||||
// InstrumentTrack::playNote
|
||||
f_cnt_t m_frames; // total frames to play
|
||||
f_cnt_t m_totalFramesPlayed; // total frame-counter - used for
|
||||
// figuring out whether a whole note
|
||||
// has been played
|
||||
f_cnt_t m_framesBeforeRelease; // number of frames after which note
|
||||
// is released
|
||||
f_cnt_t m_releaseFramesToDo; // total numbers of frames to be
|
||||
// played after release
|
||||
f_cnt_t m_releaseFramesDone; // number of frames done after
|
||||
// release of note
|
||||
NotePlayHandleList m_subNotes; // used for chords and arpeggios
|
||||
volatile bool m_released; // indicates whether note is released
|
||||
bool m_baseNote; // 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_muted; // indicates whether note is muted
|
||||
track * m_bbTrack; // related BB track
|
||||
// InstrumentTrack::playNote
|
||||
f_cnt_t m_frames; // total frames to play
|
||||
f_cnt_t m_totalFramesPlayed; // total frame-counter - used for
|
||||
// figuring out whether a whole note
|
||||
// has been played
|
||||
f_cnt_t m_framesBeforeRelease; // number of frames after which note
|
||||
// is released
|
||||
f_cnt_t m_releaseFramesToDo; // total numbers of frames to be
|
||||
// played after release
|
||||
f_cnt_t m_releaseFramesDone; // number of frames done after
|
||||
// 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_muted; // indicates whether note is muted
|
||||
track * m_bbTrack; // related BB track
|
||||
#ifdef LMMS_SINGERBOT_SUPPORT
|
||||
int m_patternIndex; // position among relevant notes
|
||||
int m_patternIndex; // position among relevant notes
|
||||
#endif
|
||||
|
||||
// tempo reaction
|
||||
bpm_t m_origTempo; // original tempo
|
||||
f_cnt_t m_origFrames; // original m_frames
|
||||
bpm_t m_origTempo; // original tempo
|
||||
f_cnt_t m_origFrames; // original m_frames
|
||||
|
||||
int m_origBaseNote;
|
||||
|
||||
float m_frequency;
|
||||
float m_unpitchedFrequency;
|
||||
|
||||
baseDetuning * m_baseDetuning;
|
||||
BaseDetuning * m_baseDetuning;
|
||||
|
||||
} ;
|
||||
|
||||
|
||||
@@ -613,7 +613,7 @@ void sf2Instrument::playNote( notePlayHandle * _n, sampleFrame * )
|
||||
}
|
||||
|
||||
fluid_synth_noteon( m_synth, m_channel, midiNote,
|
||||
_n->getMidiVelocity() );
|
||||
_n->midiVelocity() );
|
||||
|
||||
// get new voice and save it
|
||||
fluid_synth_get_voicelist( m_synth, voices, poly, -1 );
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* InstrumentFunctions.cpp - models for instrument-function-tab
|
||||
*
|
||||
* Copyright (c) 2004-2009 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
||||
* Copyright (c) 2004-2010 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
||||
*
|
||||
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
|
||||
*
|
||||
@@ -166,7 +166,7 @@ void ChordCreator::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->isBaseNote() &&
|
||||
if( ( ( _n->isTopNote() &&
|
||||
_n->instrumentTrack()->isArpeggiatorEnabled() == false ) ||
|
||||
_n->isPartOfArpeggio() ) &&
|
||||
_n->totalFramesPlayed() == 0 &&
|
||||
@@ -287,7 +287,7 @@ Arpeggiator::~Arpeggiator()
|
||||
void Arpeggiator::processNote( notePlayHandle * _n )
|
||||
{
|
||||
const int base_note_key = _n->key();
|
||||
if( _n->isBaseNote() == false ||
|
||||
if( _n->isTopNote() == false ||
|
||||
!m_arpEnabledModel.value() ||
|
||||
( _n->released() && _n->releaseFramesDone() >=
|
||||
_n->actualReleaseFramesToDo() ) )
|
||||
|
||||
@@ -33,9 +33,8 @@
|
||||
#include "song.h"
|
||||
|
||||
|
||||
inline notePlayHandle::baseDetuning::baseDetuning(
|
||||
DetuningHelper * _detuning ) :
|
||||
m_detuning( _detuning ),
|
||||
notePlayHandle::BaseDetuning::BaseDetuning( DetuningHelper *detuning ) :
|
||||
m_detuning( detuning ),
|
||||
m_value( m_detuning->automationPattern()->valueAt( 0 ) )
|
||||
{
|
||||
}
|
||||
@@ -49,7 +48,7 @@ notePlayHandle::notePlayHandle( InstrumentTrack * _it,
|
||||
const f_cnt_t _offset,
|
||||
const f_cnt_t _frames,
|
||||
const note & _n,
|
||||
notePlayHandle * _parent,
|
||||
notePlayHandle *parent,
|
||||
const bool _part_of_arp ) :
|
||||
playHandle( NotePlayHandle, _offset ),
|
||||
note( _n.length(), _n.pos(), _n.key(),
|
||||
@@ -63,34 +62,34 @@ notePlayHandle::notePlayHandle( InstrumentTrack * _it,
|
||||
m_releaseFramesToDo( 0 ),
|
||||
m_releaseFramesDone( 0 ),
|
||||
m_released( false ),
|
||||
m_baseNote( _parent == NULL ),
|
||||
m_topNote( parent == NULL ),
|
||||
m_partOfArpeggio( _part_of_arp ),
|
||||
m_muted( false ),
|
||||
m_bbTrack( NULL ),
|
||||
#ifdef LMMS_SINGERBOT_SUPPORT
|
||||
m_patternIndex( 0 ),
|
||||
#endif
|
||||
m_origTempo( engine::getSong()->getTempo() )
|
||||
m_origTempo( engine::getSong()->getTempo() ),
|
||||
m_origBaseNote( instrumentTrack()->baseNoteModel()->value() )
|
||||
{
|
||||
if( m_baseNote )
|
||||
if( isTopNote() )
|
||||
{
|
||||
m_baseDetuning = new baseDetuning( detuning() );
|
||||
m_baseDetuning = new BaseDetuning( detuning() );
|
||||
m_instrumentTrack->m_processHandles.push_back( this );
|
||||
}
|
||||
else
|
||||
{
|
||||
m_baseDetuning = _parent->m_baseDetuning;
|
||||
m_baseDetuning = parent->m_baseDetuning;
|
||||
|
||||
_parent->m_subNotes.push_back( this );
|
||||
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->isBaseNote();
|
||||
parent->m_partOfArpeggio = isPartOfArpeggio() && parent->isTopNote();
|
||||
|
||||
m_bbTrack = _parent->m_bbTrack;
|
||||
m_bbTrack = parent->m_bbTrack;
|
||||
#ifdef LMMS_SINGERBOT_SUPPORT
|
||||
m_patternIndex = _parent->m_patternIndex;
|
||||
m_patternIndex = parent->m_patternIndex;
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -99,12 +98,12 @@ notePlayHandle::notePlayHandle( InstrumentTrack * _it,
|
||||
setFrames( _frames );
|
||||
|
||||
|
||||
if( !isBaseNote() || !instrumentTrack()->isArpeggiatorEnabled() )
|
||||
if( !isTopNote() || !instrumentTrack()->isArpeggiatorEnabled() )
|
||||
{
|
||||
// send MIDI-note-on-event
|
||||
m_instrumentTrack->processOutEvent( midiEvent( MidiNoteOn,
|
||||
m_instrumentTrack->midiPort()->realOutputChannel(),
|
||||
key(), getMidiVelocity() ),
|
||||
midiKey(), midiVelocity() ),
|
||||
midiTime::fromFrames( offset(),
|
||||
engine::framesPerTick() ) );
|
||||
}
|
||||
@@ -117,7 +116,7 @@ notePlayHandle::~notePlayHandle()
|
||||
{
|
||||
noteOff( 0 );
|
||||
|
||||
if( m_baseNote )
|
||||
if( isTopNote() )
|
||||
{
|
||||
delete m_baseDetuning;
|
||||
m_instrumentTrack->m_processHandles.removeAll( this );
|
||||
@@ -151,14 +150,14 @@ void notePlayHandle::setVolume( const volume_t _volume )
|
||||
note::setVolume( _volume );
|
||||
m_instrumentTrack->processOutEvent( midiEvent( MidiKeyPressure,
|
||||
m_instrumentTrack->midiPort()->realOutputChannel(),
|
||||
key(), getMidiVelocity() ), 0 );
|
||||
midiKey(), midiVelocity() ), 0 );
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
int notePlayHandle::getMidiVelocity() const
|
||||
int notePlayHandle::midiVelocity() const
|
||||
{
|
||||
int vel = getVolume();
|
||||
if( m_instrumentTrack->getVolume() < DefaultVolume )
|
||||
@@ -171,6 +170,14 @@ int notePlayHandle::getMidiVelocity() const
|
||||
|
||||
|
||||
|
||||
int notePlayHandle::midiKey() const
|
||||
{
|
||||
return key() - m_origBaseNote + instrumentTrack()->baseNoteModel()->value();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void notePlayHandle::play( sampleFrame * _working_buffer )
|
||||
{
|
||||
if( m_muted )
|
||||
@@ -324,12 +331,12 @@ void notePlayHandle::noteOff( const f_cnt_t _s )
|
||||
m_releaseFramesToDo = qMax<f_cnt_t>( 0, // 10,
|
||||
m_instrumentTrack->m_soundShaping.releaseFrames() );
|
||||
|
||||
if( !isBaseNote() || !instrumentTrack()->isArpeggiatorEnabled() )
|
||||
if( !isTopNote() || !instrumentTrack()->isArpeggiatorEnabled() )
|
||||
{
|
||||
// send MIDI-note-off-event
|
||||
m_instrumentTrack->processOutEvent( midiEvent( MidiNoteOff,
|
||||
m_instrumentTrack->midiPort()->realOutputChannel(),
|
||||
key(), 0 ),
|
||||
midiKey(), 0 ),
|
||||
midiTime::fromFrames( m_framesBeforeRelease,
|
||||
engine::framesPerTick() ) );
|
||||
}
|
||||
@@ -372,8 +379,8 @@ float notePlayHandle::volumeLevel( const f_cnt_t _frame )
|
||||
|
||||
bool notePlayHandle::isArpeggioBaseNote() const
|
||||
{
|
||||
return isBaseNote() && ( m_partOfArpeggio ||
|
||||
m_instrumentTrack->isArpeggiatorEnabled() );
|
||||
return isTopNote() && ( m_partOfArpeggio ||
|
||||
m_instrumentTrack->isArpeggiatorEnabled() );
|
||||
}
|
||||
|
||||
|
||||
@@ -456,8 +463,9 @@ bool notePlayHandle::operator==( const notePlayHandle & _nph ) const
|
||||
offset() == _nph.offset() &&
|
||||
m_totalFramesPlayed == _nph.m_totalFramesPlayed &&
|
||||
m_released == _nph.m_released &&
|
||||
m_baseNote == _nph.m_baseNote &&
|
||||
m_topNote == _nph.m_topNote &&
|
||||
m_partOfArpeggio == _nph.m_partOfArpeggio &&
|
||||
m_origBaseNote == _nph.m_origBaseNote &&
|
||||
m_muted == _nph.m_muted;
|
||||
}
|
||||
|
||||
|
||||
@@ -903,8 +903,8 @@ void pianoRoll::keyPressEvent( QKeyEvent * _ke )
|
||||
|
||||
if( _ke->isAutoRepeat() == false && key_num > -1 )
|
||||
{
|
||||
m_pattern->instrumentTrack()->
|
||||
pianoModel()->handleKeyPress( key_num );
|
||||
m_pattern->instrumentTrack()->pianoModel()->
|
||||
handleKeyPress( key_num );
|
||||
_ke->accept();
|
||||
}
|
||||
}
|
||||
@@ -1188,8 +1188,8 @@ void pianoRoll::keyReleaseEvent( QKeyEvent * _ke )
|
||||
|
||||
if( _ke->isAutoRepeat() == false && key_num > -1 )
|
||||
{
|
||||
m_pattern->instrumentTrack()->
|
||||
pianoModel()->handleKeyRelease( key_num );
|
||||
m_pattern->instrumentTrack()->pianoModel()->
|
||||
handleKeyRelease( key_num );
|
||||
_ke->accept();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user