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.
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,22 +135,27 @@ 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;
|
||||
}
|
||||
|
||||
IntModel * baseNoteModel()
|
||||
const IntModel *baseNoteModel() const
|
||||
{
|
||||
return &m_baseNoteModel;
|
||||
}
|
||||
|
||||
Piano * getPiano()
|
||||
IntModel *baseNoteModel()
|
||||
{
|
||||
return &m_baseNoteModel;
|
||||
}
|
||||
|
||||
Piano *pianoModel()
|
||||
{
|
||||
return &m_piano;
|
||||
}
|
||||
|
||||
@@ -57,11 +57,12 @@ public:
|
||||
|
||||
virtual void setVolume( const volume_t _volume = DefaultVolume );
|
||||
|
||||
int getMidiVelocity() const;
|
||||
int midiVelocity() const;
|
||||
int midiKey() const;
|
||||
|
||||
const float & frequency() const
|
||||
{
|
||||
return( m_frequency );
|
||||
return m_frequency;
|
||||
}
|
||||
|
||||
void updateFrequency();
|
||||
@@ -69,22 +70,22 @@ public:
|
||||
// returns frequency without pitch-wheel influence
|
||||
float unpitchedFrequency() const
|
||||
{
|
||||
return( m_unpitchedFrequency );
|
||||
return m_unpitchedFrequency;
|
||||
}
|
||||
|
||||
virtual void play( sampleFrame * _working_buffer );
|
||||
|
||||
virtual inline bool done() const
|
||||
{
|
||||
return( m_released && framesLeft() <= 0 );
|
||||
return m_released && framesLeft() <= 0;
|
||||
}
|
||||
|
||||
f_cnt_t framesLeft() const;
|
||||
|
||||
inline fpp_t framesLeftForCurrentPeriod() const
|
||||
{
|
||||
return( (fpp_t) qMin<f_cnt_t>( framesLeft(),
|
||||
engine::getMixer()->framesPerPeriod() ) );
|
||||
return (fpp_t) qMin<f_cnt_t>( framesLeft(),
|
||||
engine::getMixer()->framesPerPeriod() );
|
||||
}
|
||||
|
||||
|
||||
@@ -95,12 +96,12 @@ public:
|
||||
|
||||
inline f_cnt_t framesBeforeRelease() const
|
||||
{
|
||||
return( m_framesBeforeRelease );
|
||||
return m_framesBeforeRelease;
|
||||
}
|
||||
|
||||
inline f_cnt_t releaseFramesDone() const
|
||||
{
|
||||
return( m_releaseFramesDone );
|
||||
return m_releaseFramesDone;
|
||||
}
|
||||
|
||||
f_cnt_t actualReleaseFramesToDo() const;
|
||||
@@ -109,7 +110,7 @@ public:
|
||||
// returns total numbers of frames to play
|
||||
inline f_cnt_t frames() const
|
||||
{
|
||||
return( m_frames );
|
||||
return m_frames;
|
||||
}
|
||||
|
||||
void setFrames( const f_cnt_t _frames );
|
||||
@@ -117,34 +118,39 @@ public:
|
||||
// returns whether note was released
|
||||
inline bool released() const
|
||||
{
|
||||
return( m_released );
|
||||
return m_released;
|
||||
}
|
||||
|
||||
// returns total numbers of played frames
|
||||
inline f_cnt_t totalFramesPlayed() const
|
||||
{
|
||||
return( m_totalFramesPlayed );
|
||||
return m_totalFramesPlayed;
|
||||
}
|
||||
|
||||
// returns volume-level at frame _frame (envelope/LFO)
|
||||
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
|
||||
{
|
||||
return( m_partOfArpeggio );
|
||||
return m_partOfArpeggio;
|
||||
}
|
||||
|
||||
inline void setPartOfArpeggio( const bool _on )
|
||||
@@ -157,7 +163,7 @@ public:
|
||||
|
||||
inline bool isMuted() const
|
||||
{
|
||||
return( m_muted );
|
||||
return m_muted;
|
||||
}
|
||||
|
||||
void mute();
|
||||
@@ -176,7 +182,7 @@ public:
|
||||
|
||||
inline bool bbTrackMuted()
|
||||
{
|
||||
return( m_bbTrack && m_bbTrack->isMuted() );
|
||||
return m_bbTrack && m_bbTrack->isMuted();
|
||||
}
|
||||
void setBBTrack( track * _bb_track )
|
||||
{
|
||||
@@ -189,7 +195,7 @@ public:
|
||||
#ifdef LMMS_SINGERBOT_SUPPORT
|
||||
int patternIndex()
|
||||
{
|
||||
return( m_patternIndex );
|
||||
return m_patternIndex;
|
||||
}
|
||||
|
||||
void setPatternIndex( int _i )
|
||||
@@ -200,19 +206,19 @@ 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
|
||||
{
|
||||
return( m_value );
|
||||
return m_value;
|
||||
}
|
||||
|
||||
|
||||
@@ -223,38 +229,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;
|
||||
|
||||
} ;
|
||||
|
||||
|
||||
@@ -608,7 +608,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
|
||||
*
|
||||
@@ -167,7 +167,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 &&
|
||||
@@ -288,7 +288,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;
|
||||
}
|
||||
|
||||
|
||||
@@ -898,8 +898,8 @@ void pianoRoll::keyPressEvent( QKeyEvent * _ke )
|
||||
|
||||
if( _ke->isAutoRepeat() == false && key_num > -1 )
|
||||
{
|
||||
m_pattern->instrumentTrack()->
|
||||
getPiano()->handleKeyPress( key_num );
|
||||
m_pattern->instrumentTrack()->pianoModel()->
|
||||
handleKeyPress( key_num );
|
||||
_ke->accept();
|
||||
}
|
||||
}
|
||||
@@ -1182,8 +1182,8 @@ void pianoRoll::keyReleaseEvent( QKeyEvent * _ke )
|
||||
|
||||
if( _ke->isAutoRepeat() == false && key_num > -1 )
|
||||
{
|
||||
m_pattern->instrumentTrack()->
|
||||
getPiano()->handleKeyRelease( key_num );
|
||||
m_pattern->instrumentTrack()->pianoModel()->
|
||||
handleKeyRelease( key_num );
|
||||
_ke->accept();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user