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:
Tobias Doerffel
2010-12-31 13:13:45 +01:00
parent 65a0313807
commit a9717c0cc3
6 changed files with 109 additions and 88 deletions

View File

@@ -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;
}

View File

@@ -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;
} ;

View File

@@ -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 );

View File

@@ -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() ) )

View File

@@ -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;
}

View File

@@ -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();
}
}