From 884b9ca671b8c1ea340846e97d0a82c80b721e44 Mon Sep 17 00:00:00 2001 From: Tobias Doerffel Date: Sat, 6 Sep 2008 22:04:03 +0000 Subject: [PATCH] * improved the way, MIDI-events are internally sent and handled * fixed names of various member methods of notePlayHandle class * full MIDI velocity when pressing key on test piano * send volume changes of a notePlayHandle as MidiKeyPressure events * send pitch changes of instrument track as MidiPitchBend events * added detection for running MIDI notes * correct calculation of MIDI key - makes remotePlugins respect base note settings git-svn-id: https://lmms.svn.sf.net/svnroot/lmms/trunk/lmms@1562 0778d3d1-df1d-0410-868b-ea421aaaa00d --- ChangeLog | 40 +++++++ include/instrument_track.h | 10 +- include/midi.h | 15 +++ include/note.h | 2 +- include/note_play_handle.h | 37 +++--- plugins/lb302/lb302.cpp | 2 +- src/core/instrument_functions.cpp | 8 +- src/core/note_play_handle.cpp | 86 +++++++++----- src/core/piano.cpp | 26 +++-- src/core/preset_preview_play_handle.cpp | 4 +- src/tracks/instrument_track.cpp | 147 +++++++++++++++++------- 11 files changed, 263 insertions(+), 114 deletions(-) diff --git a/ChangeLog b/ChangeLog index 335a1997e..499c363c5 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,43 @@ +2008-09-06 Tobias Doerffel + + * plugins/ladspa_effect/ladspa_subplugin_features.cpp: + * include/ladspa_base.h: + save LADSPA effect filenames without extension and add correct + one (depending on platform built for) when loading settings - fixes + missing effects when loading songs in Windows which were made in Linux + and vice versa + + * include/audio_pulseaudio.h: + added information about bad latency when using PulseAudio output + + * plugins/vestige/vestige.cpp: + * plugins/vestige/vestige.h: + only use MIDI anymore for controlling VST instruments + + * plugins/lb302/lb302.cpp: + * include/note.h: + * include/instrument_track.h: + * include/midi.h: + * include/note_play_handle.h: + * src/tracks/instrument_track.cpp: + * src/core/preset_preview_play_handle.cpp: + * src/core/note_play_handle.cpp: + * src/core/instrument_functions.cpp: + * src/core/piano.cpp: + - improved the way, MIDI-events are internally sent and handled + - fixed names of various member methods of notePlayHandle class + - full MIDI velocity when pressing key on test piano + - send volume changes of a notePlayHandle as MidiKeyPressure events + - send pitch changes of instrument track as MidiPitchBend events + - added detection for running MIDI notes + - correct calculation of MIDI key - makes remotePlugins respect + base note settings + + * src/core/midi/midi_port.cpp: + * src/core/midi/midi_alsa_seq.cpp: + fixed broken MIDI-output (when masking output events it didn't match + against correct output MIDI channel) + 2008-09-06 Csaba Hruska * plugins/sid/sid_instrument.cpp: diff --git a/include/instrument_track.h b/include/instrument_track.h index 59cac7b2f..bf1cc25f0 100644 --- a/include/instrument_track.h +++ b/include/instrument_track.h @@ -70,6 +70,8 @@ public: void processAudioBuffer( sampleFrame * _buf, const fpp_t _frames, notePlayHandle * _n ); + midiEvent applyMasterKey( const midiEvent & _me ); + virtual void processInEvent( const midiEvent & _me, const midiTime & _time ); virtual void processOutEvent( const midiEvent & _me, @@ -100,9 +102,9 @@ public: // name-stuff virtual void setName( const QString & _new_name ); - // translate key of given notePlayHandle to absolute key (i.e. - // add global master-pitch and base-note in piano) - int masterKey( notePlayHandle * _n ) const; + // translate given key of a note-event to absolute key (i.e. + // add global master-pitch and base-note of this instrument track) + int masterKey( int _midi_key ) const; // translate pitch to midi-pitch [0,16383] inline int midiPitch( void ) const @@ -181,6 +183,7 @@ protected: protected slots: void updateBaseNote( void ); + void updatePitch( void ); private: @@ -188,6 +191,7 @@ private: midiPort m_midiPort; notePlayHandle * m_notes[NumKeys]; + int m_runningMidiNotes[NumKeys]; intModel m_baseNoteModel; diff --git a/include/midi.h b/include/midi.h index 59e88a38f..4f3cf91fe 100644 --- a/include/midi.h +++ b/include/midi.h @@ -81,6 +81,7 @@ enum MidiMetaEvents const int MidiChannelCount = 16; const int MidiControllerCount = 128; +const int MidiMaxVelocity = 127; struct midiEvent @@ -105,6 +106,14 @@ struct midiEvent m_data.m_sysExDataLen = _data_len; } + midiEvent( const midiEvent & _copy ) : + m_type( _copy.m_type ), + m_channel( _copy.m_channel ), + m_data( _copy.m_data ), + m_sysExData( _copy.m_sysExData ) + { + } + inline Uint16 key( void ) const { return( m_data.m_param[0] ); @@ -125,6 +134,12 @@ struct midiEvent return( m_data.m_param[1] ); } + inline volume getVolume( void ) const + { + return( velocity() * 100 / MidiMaxVelocity ); + } + + MidiEventTypes m_type; // MIDI event type Sint8 m_channel; // MIDI channel union diff --git a/include/note.h b/include/note.h index 65ce50d49..e40910792 100644 --- a/include/note.h +++ b/include/note.h @@ -94,7 +94,7 @@ public: void setLength( const midiTime & _length ); void setPos( const midiTime & _pos ); void setKey( const int _key ); - void setVolume( const volume _volume = DefaultVolume ); + virtual void setVolume( const volume _volume = DefaultVolume ); void setPanning( const panning _panning = DefaultPanning ); void quantizeLength( const int _q_grid ); void quantizePos( const int _q_grid ); diff --git a/include/note_play_handle.h b/include/note_play_handle.h index 2c9d27eba..e4e2624bf 100644 --- a/include/note_play_handle.h +++ b/include/note_play_handle.h @@ -52,9 +52,12 @@ public: const f_cnt_t _offset, const f_cnt_t _frames, const note & _n, notePlayHandle * _parent = NULL, - const bool _arp_note = FALSE ); + const bool _part_of_arp = false ); virtual ~notePlayHandle(); + virtual void setVolume( const volume _volume = DefaultVolume ); + + int getMidiVelocity( void ) const; const float & frequency( void ) const { @@ -85,7 +88,7 @@ public: if( m_released == TRUE ) { f_cnt_t todo = engine::getMixer()->framesPerPeriod(); - if( arpBaseNote() == TRUE ) + if( isArpeggioBaseNote() ) { rftd = rfd + 2 * engine::getMixer()->framesPerPeriod(); @@ -118,7 +121,7 @@ public: } } - if( arpBaseNote() == TRUE && m_subNotes.size() == 0 ) + if( isArpeggioBaseNote() && m_subNotes.size() == 0 ) { rfd = rftd; } @@ -184,29 +187,29 @@ public: // returns whether note is a base-note, e.g. is not part of an arpeggio // or a chord - inline bool baseNote( void ) const + inline bool isBaseNote( void ) const { return( m_baseNote ); } - // returns whether note is part of an arpeggio - inline bool arpNote( void ) const + inline bool isPartOfArpeggio( void ) const { - return( m_arpNote ); + return( m_partOfArpeggio ); } - inline void setArpNote( const bool _on ) + inline void setPartOfArpeggio( const bool _on ) { - m_arpNote = _on; + m_partOfArpeggio = _on; } // returns whether note is base-note for arpeggio - inline bool arpBaseNote( void ) const + inline bool isArpeggioBaseNote( void ) const { - return( baseNote() && arpNote() ); + return( isBaseNote() && ( m_partOfArpeggio || + m_instrumentTrack->arpeggiatorEnabled() ) ); } - inline bool muted( void ) const + inline bool isMuted( void ) const { return( m_muted ); } @@ -214,7 +217,7 @@ public: void mute( void ); // returns index of note-play-handle in vector of note-play-handles - // belonging to this channel + // belonging to this instrument-track - used by arpeggiator int index( void ) const; // note-play-handles belonging to given channel, if _all_ph = TRUE, @@ -255,7 +258,7 @@ public: } void processMidiTime( const midiTime & _time ); - void resize( const bpm_t _new_bpm ); + void resize( const bpm_t _new_tempo ); #if LMMS_SINGERBOT_SUPPORT int patternIndex( void ) @@ -310,7 +313,7 @@ private: 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_arpNote; // indicates whether note is part of + 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 @@ -320,8 +323,8 @@ private: #endif // tempo reaction - bpm_t m_orig_bpm; // original bpm - f_cnt_t m_orig_frames; // original m_frames + bpm_t m_origTempo; // original tempo + f_cnt_t m_origFrames; // original m_frames float m_frequency; float m_unpitchedFrequency; diff --git a/plugins/lb302/lb302.cpp b/plugins/lb302/lb302.cpp index 9e5705143..18c6e3599 100644 --- a/plugins/lb302/lb302.cpp +++ b/plugins/lb302/lb302.cpp @@ -706,7 +706,7 @@ void lb302Synth::playNote( notePlayHandle * _n, bool, { fpp_t framesPerPeriod = engine::getMixer()->framesPerPeriod(); - if( _n->arpBaseNote() ) + if( _n->isArpeggioBaseNote() ) { return; } diff --git a/src/core/instrument_functions.cpp b/src/core/instrument_functions.cpp index 4de8bf6f5..ac8ad9846 100644 --- a/src/core/instrument_functions.cpp +++ b/src/core/instrument_functions.cpp @@ -187,9 +187,9 @@ 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->baseNote() && + if( ( ( _n->isBaseNote() && _n->getInstrumentTrack()->arpeggiatorEnabled() == FALSE ) || - _n->arpNote() ) && + _n->isPartOfArpeggio() ) && _n->totalFramesPlayed() == 0 && m_chordsEnabledModel.value() == TRUE ) { @@ -310,7 +310,7 @@ arpeggiator::~arpeggiator() void arpeggiator::processNote( notePlayHandle * _n ) { const int base_note_key = _n->key(); - if( _n->baseNote() == FALSE || + if( _n->isBaseNote() == FALSE || !m_arpEnabledModel.value() || ( _n->released() && _n->releaseFramesDone() >= _n->actualReleaseFramesToDo() ) ) @@ -467,7 +467,7 @@ void arpeggiator::processNote( notePlayHandle * _n ) // sub-note so far if( m_arpModeModel.value() != FreeMode ) { - _n->setArpNote( TRUE ); + _n->setPartOfArpeggio( true ); } } diff --git a/src/core/note_play_handle.cpp b/src/core/note_play_handle.cpp index 7feb1b42b..f5872027a 100644 --- a/src/core/note_play_handle.cpp +++ b/src/core/note_play_handle.cpp @@ -53,7 +53,7 @@ notePlayHandle::notePlayHandle( instrumentTrack * _it, const f_cnt_t _frames, const note & _n, notePlayHandle * _parent, - const bool _arp_note ) : + const bool _part_of_arp ) : playHandle( NotePlayHandle, _offset ), note( _n.length(), _n.pos(), _n.key(), _n.getVolume(), _n.getPanning(), _n.detuning() ), @@ -67,13 +67,13 @@ notePlayHandle::notePlayHandle( instrumentTrack * _it, m_releaseFramesDone( 0 ), m_released( FALSE ), m_baseNote( _parent == NULL ), - m_arpNote( _arp_note ), + m_partOfArpeggio( _part_of_arp ), m_muted( FALSE ), m_bbTrack( NULL ), #if LMMS_SINGERBOT_SUPPORT m_patternIndex( 0 ), #endif - m_orig_bpm( engine::getSong()->getTempo() ) + m_origTempo( engine::getSong()->getTempo() ) { if( m_baseNote ) { @@ -88,7 +88,8 @@ notePlayHandle::notePlayHandle( instrumentTrack * _it, // 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_arpNote = arpNote() && _parent->baseNote(); + _parent->m_partOfArpeggio = isPartOfArpeggio() && + _parent->isBaseNote(); m_bbTrack = _parent->m_bbTrack; #if LMMS_SINGERBOT_SUPPORT @@ -99,16 +100,17 @@ notePlayHandle::notePlayHandle( instrumentTrack * _it, updateFrequency(); setFrames( _frames ); - // send MIDI-note-on-event - m_instrumentTrack->processOutEvent( midiEvent( MidiNoteOn, + + + if( !isBaseNote() || !getInstrumentTrack()->arpeggiatorEnabled() ) + { + // send MIDI-note-on-event + m_instrumentTrack->processOutEvent( midiEvent( MidiNoteOn, m_instrumentTrack->getMidiPort()->outputChannel(), - key(), - tLimit( - (Uint16) ( ( getVolume() / 100.0f ) * - ( m_instrumentTrack->getVolume() / 100.0f ) * - 127 ), 0, 127 ) ), - midiTime::fromFrames( offset(), + key(), getMidiVelocity() ), + midiTime::fromFrames( offset(), engine::framesPerTick() ) ); + } } @@ -147,6 +149,29 @@ notePlayHandle::~notePlayHandle() +void notePlayHandle::setVolume( const volume _volume ) +{ + note::setVolume( _volume ); + m_instrumentTrack->processOutEvent( midiEvent( MidiKeyPressure, + m_instrumentTrack->getMidiPort()->outputChannel(), + key(), getMidiVelocity() ), 0 ); + +} + + + + +int notePlayHandle::getMidiVelocity( void ) const +{ + return tLimit( (Uint16) ( ( getVolume() / 100.0f ) * + ( m_instrumentTrack->getVolume() / 100.0f ) * + MidiMaxVelocity ), + 0, MidiMaxVelocity ); +} + + + + void notePlayHandle::play( bool _try_parallelizing, sampleFrame * _working_buffer ) { @@ -180,7 +205,7 @@ void notePlayHandle::play( bool _try_parallelizing, // because we do not allow notePlayHandle::done() to be true // until all sub-notes are completely played and no new ones // are inserted by arpAndChordsTabWidget::processNote() - if( arpBaseNote() == TRUE ) + if( isArpeggioBaseNote() ) { m_releaseFramesToDo = m_releaseFramesDone + 2 * engine::getMixer()->framesPerPeriod(); @@ -246,7 +271,7 @@ void notePlayHandle::play( bool _try_parallelizing, // can set m_releaseFramesDone to m_releaseFramesToDo so that // notePlayHandle::done() returns true and also this base-note is // removed from mixer's active note vector - if( arpBaseNote() == TRUE && m_subNotes.size() == 0 ) + if( isArpeggioBaseNote() && m_subNotes.size() == 0 ) { m_releaseFramesDone = m_releaseFramesToDo; } @@ -301,12 +326,16 @@ void notePlayHandle::noteOff( const f_cnt_t _s ) m_framesBeforeRelease = _s; m_releaseFramesToDo = tMax( 0, // 10, m_instrumentTrack->m_soundShaping.releaseFrames() ); - // send MIDI-note-off-event - m_instrumentTrack->processOutEvent( midiEvent( MidiNoteOff, + + if( !isBaseNote() || !getInstrumentTrack()->arpeggiatorEnabled() ) + { + // send MIDI-note-off-event + m_instrumentTrack->processOutEvent( midiEvent( MidiNoteOff, m_instrumentTrack->getMidiPort()->outputChannel(), key(), 0 ), midiTime::fromFrames( m_framesBeforeRelease, engine::framesPerTick() ) ); + } m_released = TRUE; } @@ -317,7 +346,7 @@ void notePlayHandle::noteOff( const f_cnt_t _s ) f_cnt_t notePlayHandle::actualReleaseFramesToDo( void ) const { return( m_instrumentTrack->m_soundShaping.releaseFrames( - arpBaseNote() ) ); + isArpeggioBaseNote() ) ); } @@ -330,7 +359,7 @@ void notePlayHandle::setFrames( const f_cnt_t _frames ) { m_frames = m_instrumentTrack->beatLen( this ); } - m_orig_frames = m_frames; + m_origFrames = m_frames; } @@ -421,7 +450,7 @@ bool notePlayHandle::operator==( const notePlayHandle & _nph ) const m_totalFramesPlayed == _nph.m_totalFramesPlayed && m_released == _nph.m_released && m_baseNote == _nph.m_baseNote && - m_arpNote == _nph.m_arpNote && + m_partOfArpeggio == _nph.m_partOfArpeggio && m_muted == _nph.m_muted ); } @@ -430,14 +459,9 @@ bool notePlayHandle::operator==( const notePlayHandle & _nph ) const void notePlayHandle::updateFrequency( void ) { - const int base_tone = m_instrumentTrack->baseNoteModel()->value() % - KeysPerOctave; - const int base_octave = m_instrumentTrack->baseNoteModel()->value() / - KeysPerOctave; - const float pitch = ( key() % KeysPerOctave - base_tone + - engine::getSong()->masterPitch() ) / 12.0f + - ( key() / KeysPerOctave - base_octave ) + - m_baseDetuning->value() / 12.0f; + const float pitch = + ( key() - m_instrumentTrack->baseNoteModel()->value() + + engine::getSong()->masterPitch() ) / 12.0f; m_frequency = BaseFreq * powf( 2.0f, pitch + m_instrumentTrack->m_pitchModel.value() / ( 100 * 12.0 ) ); m_unpitchedFrequency = BaseFreq * powf( 2.0f, pitch ); @@ -469,17 +493,17 @@ void notePlayHandle::processMidiTime( const midiTime & _time ) -void notePlayHandle::resize( const bpm_t _new_bpm ) +void notePlayHandle::resize( const bpm_t _new_tempo ) { - double completed = m_totalFramesPlayed / (double)m_frames; - double new_frames = m_orig_frames * m_orig_bpm / (double)_new_bpm; + double completed = m_totalFramesPlayed / (double) m_frames; + double new_frames = m_origFrames * m_origTempo / (double) _new_tempo; m_frames = (f_cnt_t)new_frames; m_totalFramesPlayed = (f_cnt_t)( completed * new_frames ); for( notePlayHandleVector::iterator it = m_subNotes.begin(); it != m_subNotes.end(); ++it ) { - ( *it )->resize( _new_bpm ); + ( *it )->resize( _new_tempo ); } } diff --git a/src/core/piano.cpp b/src/core/piano.cpp index a22bb6dc2..2887cf972 100644 --- a/src/core/piano.cpp +++ b/src/core/piano.cpp @@ -149,7 +149,7 @@ void piano::setKeyState( int _key, bool _on ) void piano::handleKeyPress( int _key ) { m_instrumentTrack->processInEvent( midiEvent( MidiNoteOn, 0, _key, - DefaultVolume ), midiTime() ); + MidiMaxVelocity ), midiTime() ); m_pressedKeys[_key] = TRUE; } @@ -478,26 +478,26 @@ void pianoView::mousePressEvent( QMouseEvent * _me ) if( _me->pos().y() > PIANO_BASE ) { int y_diff = _me->pos().y() - PIANO_BASE; - volume vol = (volume)( ( float ) y_diff / + int velocity = (int)( ( float ) y_diff / ( ( KEY_ORDER[key_num % KeysPerOctave] == WhiteKey ) ? PW_WHITE_KEY_HEIGHT : PW_BLACK_KEY_HEIGHT ) * - (float) DefaultVolume ); + (float) MidiMaxVelocity ); if( y_diff < 0 ) { - vol = 0; + velocity = 0; } else if( y_diff > ( ( KEY_ORDER[key_num % KeysPerOctave] == WhiteKey ) ? PW_WHITE_KEY_HEIGHT : PW_BLACK_KEY_HEIGHT ) ) { - vol = DefaultVolume; + velocity = MidiMaxVelocity; } // set note on m_piano->m_instrumentTrack->processInEvent( midiEvent( MidiNoteOn, 0, key_num, - vol * 127 / 100 ), + velocity ), midiTime() ); m_piano->m_pressedKeys[key_num] = TRUE; m_lastKey = key_num; @@ -581,22 +581,22 @@ void pianoView::mouseMoveEvent( QMouseEvent * _me ) int key_num = getKeyFromMouse( _me->pos() ); int y_diff = _me->pos().y() - PIANO_BASE; - volume vol = (volume)( (float) y_diff / + int velocity = (int)( (float) y_diff / ( ( KEY_ORDER[key_num % KeysPerOctave] == WhiteKey ) ? PW_WHITE_KEY_HEIGHT : PW_BLACK_KEY_HEIGHT ) * - (float)DefaultVolume ); + (float) MidiMaxVelocity ); // maybe the user moved the mouse-cursor above or under the // piano-widget while holding left button so check that and // correct volume if necessary if( y_diff < 0 ) { - vol = 0; + velocity = 0; } else if( y_diff > ( ( KEY_ORDER[key_num % KeysPerOctave] == WhiteKey ) ? PW_WHITE_KEY_HEIGHT : PW_BLACK_KEY_HEIGHT ) ) { - vol = DefaultVolume; + velocity = MidiMaxVelocity; } // is the calculated key different from current key? (could be the @@ -616,7 +616,8 @@ void pianoView::mouseMoveEvent( QMouseEvent * _me ) if( _me->pos().y() > PIANO_BASE ) { m_piano->m_instrumentTrack->processInEvent( - midiEvent( MidiNoteOn, 0, key_num, vol ), + midiEvent( MidiNoteOn, 0, key_num, + velocity ), midiTime() ); m_piano->m_pressedKeys[key_num] = TRUE; m_lastKey = key_num; @@ -633,7 +634,8 @@ void pianoView::mouseMoveEvent( QMouseEvent * _me ) else if( m_piano->m_pressedKeys[key_num] == TRUE ) { m_piano->m_instrumentTrack->processInEvent( - midiEvent( MidiKeyPressure, 0, key_num, vol ), + midiEvent( MidiKeyPressure, 0, key_num, + velocity ), midiTime() ); } diff --git a/src/core/preset_preview_play_handle.cpp b/src/core/preset_preview_play_handle.cpp index e21020cff..98048dc84 100644 --- a/src/core/preset_preview_play_handle.cpp +++ b/src/core/preset_preview_play_handle.cpp @@ -170,7 +170,7 @@ presetPreviewPlayHandle::~presetPreviewPlayHandle() { s_previewTC->lockData(); // not muted by other preset-preview-handle? - if( m_previewNote->muted() == FALSE ) + if( !m_previewNote->isMuted() ) { // then set according state s_previewTC->setPreviewNote( NULL ); @@ -193,7 +193,7 @@ void presetPreviewPlayHandle::play( bool _try_parallelizing, bool presetPreviewPlayHandle::done( void ) const { - return( m_previewNote->muted() ); + return( m_previewNote->isMuted() ); } diff --git a/src/tracks/instrument_track.cpp b/src/tracks/instrument_track.cpp index e4e5a8265..dfe20da38 100644 --- a/src/tracks/instrument_track.cpp +++ b/src/tracks/instrument_track.cpp @@ -116,12 +116,13 @@ instrumentTrack::instrumentTrack( trackContainer * _tc ) : connect( &m_baseNoteModel, SIGNAL( dataChanged() ), this, SLOT( updateBaseNote() ) ); connect( &m_pitchModel, SIGNAL( dataChanged() ), - this, SLOT( updateBaseNote() ) ); + this, SLOT( updatePitch() ) ); for( int i = 0; i < NumKeys; ++i ) { m_notes[i] = NULL; + m_runningMidiNotes[i] = 0; } @@ -141,24 +142,6 @@ instrumentTrack::~instrumentTrack() -f_cnt_t instrumentTrack::beatLen( notePlayHandle * _n ) const -{ - if( m_instrument != NULL ) - { - const f_cnt_t len = m_instrument->beatLen( _n ); - if( len > 0 ) - { - return( len ); - } - } - return( m_soundShaping.envFrames() ); -} - - - - - - void instrumentTrack::processAudioBuffer( sampleFrame * _buf, const fpp_t _frames, notePlayHandle * _n ) @@ -197,15 +180,37 @@ void instrumentTrack::processAudioBuffer( sampleFrame * _buf, +midiEvent instrumentTrack::applyMasterKey( const midiEvent & _me ) +{ + midiEvent copy( _me ); + switch( _me.m_type ) + { + case MidiNoteOn: + case MidiNoteOff: + case MidiKeyPressure: + copy.key() = masterKey( _me.key() ); + break; + default: + break; + } + return copy; +} + + + + void instrumentTrack::processInEvent( const midiEvent & _me, const midiTime & _time ) { + engine::getMixer()->lock(); switch( _me.m_type ) { + // we don't send MidiNoteOn, MidiNoteOff and MidiKeyPressure + // events to instrument as notePlayHandle will send them on its + // own case MidiNoteOn: if( _me.velocity() > 0 ) { - m_instrument->handleMidiEvent( _me, _time ); if( m_notes[_me.key()] == NULL ) { if( !configManager::inst()->value( "ui", @@ -217,8 +222,8 @@ void instrumentTrack::processInEvent( const midiEvent & _me, // create temporary note note n; n.setKey( _me.key() ); - n.setVolume( _me.velocity() * 100 / - 127 ); + n.setVolume( _me.getVolume() ); + // create (timed) note-play-handle notePlayHandle * nph = new notePlayHandle( this, @@ -231,14 +236,12 @@ void instrumentTrack::processInEvent( const midiEvent & _me, { m_notes[_me.key()] = nph; } - return; } break; } case MidiNoteOff: { - m_instrument->handleMidiEvent( _me, _time ); notePlayHandle * n = m_notes[_me.key()]; if( n != NULL ) { @@ -248,11 +251,13 @@ void instrumentTrack::processInEvent( const midiEvent & _me, // this is for example needed by piano-roll for // recording notes into a pattern note done_note( - midiTime( static_cast( - n->totalFramesPlayed() / - engine::framesPerTick() ) ), - 0, n->key(), - n->getVolume(), n->getPanning() ); + midiTime( static_cast( + n->totalFramesPlayed() / + engine::framesPerTick() ) ), + 0, + n->key(), + n->getVolume(), + n->getPanning() ); n->noteOff(); m_notes[_me.key()] = NULL; @@ -263,16 +268,16 @@ void instrumentTrack::processInEvent( const midiEvent & _me, } case MidiKeyPressure: - if( !m_instrument->handleMidiEvent( _me, _time ) && - m_notes[_me.key()] != NULL ) + if( m_notes[_me.key()] != NULL ) { - m_notes[_me.key()]->setVolume( _me.velocity() * - 100 / 127 ); + m_notes[_me.key()]->setVolume( _me.getVolume() ); } break; case MidiPitchBend: - m_instrument->handleMidiEvent( _me, _time ); + // updatePitch() is connected to + // m_pitchModel::dataChanged() which will send out + // MidiPitchBend events m_pitchModel.setValue( m_pitchModel.minValue() + _me.m_data.m_param[0] * m_pitchModel.range() / 16384 ); @@ -291,6 +296,7 @@ void instrumentTrack::processInEvent( const midiEvent & _me, } break; } + engine::getMixer()->unlock(); } @@ -299,6 +305,8 @@ void instrumentTrack::processInEvent( const midiEvent & _me, void instrumentTrack::processOutEvent( const midiEvent & _me, const midiTime & _time ) { + int k; + switch( _me.m_type ) { case MidiNoteOn: @@ -310,11 +318,23 @@ void instrumentTrack::processOutEvent( const midiEvent & _me, if( !configManager::inst()->value( "ui", "disablechannelactivityindicators" ).toInt() ) { - if( m_notes[_me.key()] != NULL ) + if( m_notes[_me.key()] == NULL ) { - return; + emit newNote(); } - emit newNote(); + } + k = masterKey( _me.key() ); + if( k >= 0 && k < NumKeys ) + { + if( m_runningMidiNotes[k] > 0 ) + { + m_instrument->handleMidiEvent( + midiEvent( MidiNoteOff, getMidiPort()->outputChannel(), k, 0 ), _time ); + } + ++m_runningMidiNotes[k]; + m_instrument->handleMidiEvent( + midiEvent( MidiNoteOn, getMidiPort()->outputChannel(), k, + _me.velocity() ), _time ); } break; @@ -324,12 +344,25 @@ void instrumentTrack::processOutEvent( const midiEvent & _me, { m_piano.setKeyState( _me.key(), FALSE ); } + k = masterKey( _me.key() ); + if( k >= 0 && k < NumKeys && + --m_runningMidiNotes[k] <= 0 ) + { + m_instrument->handleMidiEvent( + midiEvent( MidiNoteOff, getMidiPort()->outputChannel(), k, 0 ), _time ); + } break; default: + if( m_instrument != NULL ) + { + m_instrument->handleMidiEvent( + applyMasterKey( _me ), + _time ); + } break; } - m_instrument->handleMidiEvent( _me, _time ); + // if appropriate, midi-port does futher routing m_midiPort.processOutEvent( _me, _time ); } @@ -337,6 +370,22 @@ void instrumentTrack::processOutEvent( const midiEvent & _me, +f_cnt_t instrumentTrack::beatLen( notePlayHandle * _n ) const +{ + if( m_instrument != NULL ) + { + const f_cnt_t len = m_instrument->beatLen( _n ); + if( len > 0 ) + { + return( len ); + } + } + return( m_soundShaping.envFrames() ); +} + + + + void instrumentTrack::playNote( notePlayHandle * _n, bool _try_parallelizing, sampleFrame * _working_buffer ) { @@ -345,7 +394,7 @@ void instrumentTrack::playNote( notePlayHandle * _n, bool _try_parallelizing, m_chordCreator.processNote( _n ); m_arpeggiator.processNote( _n ); - if( _n->arpBaseNote() == FALSE && m_instrument != NULL ) + if( !_n->isArpeggioBaseNote() && m_instrument != NULL ) { // all is done, so now lets play the note! m_instrument->playNote( _n, _try_parallelizing, @@ -431,11 +480,22 @@ void instrumentTrack::updateBaseNote( void ) -int instrumentTrack::masterKey( notePlayHandle * _n ) const +void instrumentTrack::updatePitch( void ) +{ + updateBaseNote(); + processOutEvent( midiEvent( MidiPitchBend, + getMidiPort()->outputChannel(), + midiPitch() ), 0 ); +} + + + + +int instrumentTrack::masterKey( int _midi_key ) const { int key = m_baseNoteModel.value() + engine::getSong()->masterPitch(); - return( tLimit( _n->key() - - ( key - Key_A - DefaultOctave * KeysPerOctave ), 0, NumKeys ) ); + return( tLimit( _midi_key - + ( key - DefaultKey ), 0, NumKeys ) ); } @@ -986,7 +1046,8 @@ void instrumentTrackView::toggleInstrumentWindow( bool _on ) void instrumentTrackView::activityIndicatorPressed( void ) { - model()->processInEvent( midiEvent( MidiNoteOn, 0, DefaultKey, 127 ), + model()->processInEvent( + midiEvent( MidiNoteOn, 0, DefaultKey, MidiMaxVelocity ), midiTime() ); }