MidiPort: introduced internal base velocity property
In order to keep compatibility with projects created with LMMS < 1.0.0 we maintain a property specifying the base velocity (i.e. the velocity sent to MIDI-based instruments at volume=100%). For new projects this always will be 64 while compat code enforces a value of 127 for old projects. We can also think about hiding the new groupbox in order to hide complexity from the user. Closes #430.
This commit is contained in:
@@ -58,7 +58,8 @@ private:
|
||||
LcdSpinBox * m_fixedOutputNoteSpinBox;
|
||||
QToolButton * m_wpBtn;
|
||||
|
||||
LcdSpinBox* m_baseVelocitySpinBox;
|
||||
|
||||
} ;
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
@@ -140,9 +140,9 @@ public:
|
||||
return m_data.m_param[1];
|
||||
}
|
||||
|
||||
volume_t volume() const
|
||||
volume_t volume( int midiBaseVelocity ) const
|
||||
{
|
||||
return (volume_t)( velocity() * MaxVolume / MidiMaxVelocity );
|
||||
return (volume_t)( velocity() * DefaultVolume / midiBaseVelocity );
|
||||
}
|
||||
|
||||
const void* sourcePort() const
|
||||
|
||||
@@ -53,6 +53,7 @@ class MidiPort : public Model, public SerializingObject
|
||||
mapPropertyFromModel(int,fixedOutputVelocity,setFixedOutputVelocity,m_fixedOutputVelocityModel);
|
||||
mapPropertyFromModel(int,fixedOutputNote,setFixedOutputNote,m_fixedOutputNoteModel);
|
||||
mapPropertyFromModel(int,outputProgram,setOutputProgram,m_outputProgramModel);
|
||||
mapPropertyFromModel(int,baseVelocity,setBaseVelocity,m_baseVelocityModel);
|
||||
mapPropertyFromModel(bool,isReadable,setReadable,m_readableModel);
|
||||
mapPropertyFromModel(bool,isWritable,setWritable,m_writableModel);
|
||||
public:
|
||||
@@ -151,6 +152,7 @@ private:
|
||||
IntModel m_fixedOutputVelocityModel;
|
||||
IntModel m_fixedOutputNoteModel;
|
||||
IntModel m_outputProgramModel;
|
||||
IntModel m_baseVelocityModel;
|
||||
BoolModel m_readableModel;
|
||||
BoolModel m_writableModel;
|
||||
|
||||
|
||||
@@ -176,9 +176,9 @@ public:
|
||||
return m_volume;
|
||||
}
|
||||
|
||||
int midiVelocity() const
|
||||
int midiVelocity( int midiBaseVelocity ) const
|
||||
{
|
||||
return qMin( MidiMaxVelocity, getVolume() * MidiMaxVelocity / MaxVolume );
|
||||
return qMin( MidiMaxVelocity, getVolume() * midiBaseVelocity / DefaultVolume );
|
||||
}
|
||||
|
||||
inline panning_t getPanning() const
|
||||
|
||||
@@ -565,8 +565,9 @@ void sf2Instrument::playNote( NotePlayHandle * _n, sampleFrame * )
|
||||
id[i] = fluid_voice_get_id( voices[i] );
|
||||
}
|
||||
|
||||
fluid_synth_noteon( m_synth, m_channel, midiNote,
|
||||
_n->midiVelocity() );
|
||||
const int baseVelocity = instrumentTrack()->midiPort()->baseVelocity();
|
||||
|
||||
fluid_synth_noteon( m_synth, m_channel, midiNote, _n->midiVelocity( baseVelocity ) );
|
||||
|
||||
// get new voice and save it
|
||||
fluid_synth_get_voicelist( m_synth, voices, poly, -1 );
|
||||
|
||||
@@ -106,9 +106,11 @@ NotePlayHandle::NotePlayHandle( InstrumentTrack* instrumentTrack,
|
||||
|
||||
if( !isTopNote() || !instrumentTrack->isArpeggioEnabled() )
|
||||
{
|
||||
const int baseVelocity = m_instrumentTrack->midiPort()->baseVelocity();
|
||||
|
||||
// send MidiNoteOn event
|
||||
m_instrumentTrack->processOutEvent(
|
||||
MidiEvent( MidiNoteOn, midiChannel(), midiKey(), midiVelocity() ),
|
||||
MidiEvent( MidiNoteOn, midiChannel(), midiKey(), midiVelocity( baseVelocity ) ),
|
||||
MidiTime::fromFrames( offset(), engine::framesPerTick() ) );
|
||||
}
|
||||
}
|
||||
@@ -152,7 +154,9 @@ void NotePlayHandle::setVolume( volume_t _volume )
|
||||
{
|
||||
note::setVolume( _volume );
|
||||
|
||||
m_instrumentTrack->processOutEvent( MidiEvent( MidiKeyPressure, midiChannel(), midiKey(), midiVelocity() ) );
|
||||
const int baseVelocity = m_instrumentTrack->midiPort()->baseVelocity();
|
||||
|
||||
m_instrumentTrack->processOutEvent( MidiEvent( MidiKeyPressure, midiChannel(), midiKey(), midiVelocity( baseVelocity ) ) );
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -50,6 +50,7 @@ MidiPort::MidiPort( const QString& name,
|
||||
m_fixedOutputVelocityModel( -1, -1, MidiMaxVelocity, this, tr( "Fixed output velocity" ) ),
|
||||
m_fixedOutputNoteModel( -1, -1, MidiMaxNote, this, tr( "Fixed output note" ) ),
|
||||
m_outputProgramModel( 1, 1, MidiProgramCount, this, tr( "Output MIDI program" ) ),
|
||||
m_baseVelocityModel( MidiMaxVelocity/2, 1, MidiMaxVelocity, this, tr( "Base velocity" ) ),
|
||||
m_readableModel( false, this, tr( "Receive MIDI-events" ) ),
|
||||
m_writableModel( false, this, tr( "Send MIDI-events" ) )
|
||||
{
|
||||
@@ -171,6 +172,7 @@ void MidiPort::saveSettings( QDomDocument& doc, QDomElement& thisElement )
|
||||
m_fixedOutputVelocityModel.saveSettings( doc, thisElement, "fixedoutputvelocity" );
|
||||
m_fixedOutputNoteModel.saveSettings( doc, thisElement, "fixedoutputnote" );
|
||||
m_outputProgramModel.saveSettings( doc, thisElement, "outputprogram" );
|
||||
m_baseVelocityModel.saveSettings( doc, thisElement, "basevelocity" );
|
||||
m_readableModel.saveSettings( doc, thisElement, "readable" );
|
||||
m_writableModel.saveSettings( doc, thisElement, "writable" );
|
||||
|
||||
@@ -223,6 +225,7 @@ void MidiPort::loadSettings( const QDomElement& thisElement )
|
||||
m_fixedInputVelocityModel.loadSettings( thisElement, "fixedinputvelocity" );
|
||||
m_fixedOutputVelocityModel.loadSettings( thisElement, "fixedoutputvelocity" );
|
||||
m_outputProgramModel.loadSettings( thisElement, "outputprogram" );
|
||||
m_baseVelocityModel.loadSettings( thisElement, "basevelocity" );
|
||||
m_readableModel.loadSettings( thisElement, "readable" );
|
||||
m_writableModel.loadSettings( thisElement, "writable" );
|
||||
|
||||
@@ -253,11 +256,20 @@ void MidiPort::loadSettings( const QDomElement& thisElement )
|
||||
}
|
||||
emit writablePortsChanged();
|
||||
}
|
||||
|
||||
if( thisElement.hasAttribute( "basevelocity" ) == false )
|
||||
{
|
||||
// for projects created by LMMS < 0.9.92 there's no value for the base
|
||||
// velocity and for compat reasons we have to stick with maximum velocity
|
||||
// which did not allow note volumes > 100%
|
||||
m_baseVelocityModel.setValue( MidiMaxVelocity );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void MidiPort::subscribeReadablePort( const QString& port, bool subscribe )
|
||||
{
|
||||
m_readablePorts[port] = subscribe;
|
||||
|
||||
@@ -1864,7 +1864,10 @@ void PianoRoll::testPlayNote( note * n )
|
||||
if( n->isPlaying() == false && m_recording == false )
|
||||
{
|
||||
n->setIsPlaying( true );
|
||||
m_pattern->instrumentTrack()->pianoModel()->handleKeyPress( n->key(), n->midiVelocity() );
|
||||
|
||||
const int baseVelocity = m_pattern->instrumentTrack()->midiPort()->baseVelocity();
|
||||
|
||||
m_pattern->instrumentTrack()->pianoModel()->handleKeyPress( n->key(), n->midiVelocity( baseVelocity ) );
|
||||
|
||||
MidiEvent event( MidiMetaEvent, 0, n->key(), panningToMidi( n->getPanning() ) );
|
||||
|
||||
@@ -2225,7 +2228,10 @@ void PianoRoll::mouseMoveEvent( QMouseEvent * _me )
|
||||
if( m_noteEditMode == NoteEditVolume )
|
||||
{
|
||||
n->setVolume( vol );
|
||||
m_pattern->instrumentTrack()->processInEvent( MidiEvent( MidiKeyPressure, 0, n->key(), n->midiVelocity() ) );
|
||||
|
||||
const int baseVelocity = m_pattern->instrumentTrack()->midiPort()->baseVelocity();
|
||||
|
||||
m_pattern->instrumentTrack()->processInEvent( MidiEvent( MidiKeyPressure, 0, n->key(), n->midiVelocity( baseVelocity ) ) );
|
||||
}
|
||||
else if( m_noteEditMode == NoteEditPanning )
|
||||
{
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
|
||||
#include <QtGui/QMenu>
|
||||
#include <QtGui/QToolButton>
|
||||
#include <QtGui/QLabel>
|
||||
#include <QtGui/QLayout>
|
||||
|
||||
#include "InstrumentMidiIOView.h"
|
||||
@@ -135,6 +136,30 @@ InstrumentMidiIOView::InstrumentMidiIOView( QWidget* parent ) :
|
||||
midiOutputLayout->insertWidget( 0, m_wpBtn );
|
||||
}
|
||||
|
||||
#define PROVIDE_CUSTOM_BASE_VELOCITY_UI
|
||||
#ifdef PROVIDE_CUSTOM_BASE_VELOCITY_UI
|
||||
groupBox* baseVelocityGroupBox = new groupBox( tr( "CUSTOM BASE VELOCITY" ) );
|
||||
layout->addWidget( baseVelocityGroupBox );
|
||||
|
||||
QVBoxLayout* baseVelocityLayout = new QVBoxLayout( baseVelocityGroupBox );
|
||||
baseVelocityLayout->setContentsMargins( 8, 18, 8, 8 );
|
||||
baseVelocityLayout->setSpacing( 6 );
|
||||
|
||||
QLabel* baseVelocityHelp = new QLabel( tr( "Specify the velocity normalization base for MIDI-based instruments at note volume 100%" ) );
|
||||
baseVelocityHelp->setWordWrap( true );
|
||||
baseVelocityHelp->setFont( pointSize<8>( baseVelocityHelp->font() ) );
|
||||
|
||||
baseVelocityLayout->addWidget( baseVelocityHelp );
|
||||
|
||||
m_baseVelocitySpinBox = new LcdSpinBox( 3, baseVelocityGroupBox );
|
||||
m_baseVelocitySpinBox->setLabel( tr( "BASE VELOCITY" ) );
|
||||
m_baseVelocitySpinBox->setEnabled( false );
|
||||
baseVelocityLayout->addWidget( m_baseVelocitySpinBox );
|
||||
|
||||
connect( baseVelocityGroupBox->ledButton(), SIGNAL( toggled( bool ) ),
|
||||
m_baseVelocitySpinBox, SLOT( setEnabled( bool ) ) );
|
||||
#endif
|
||||
|
||||
layout->addStretch();
|
||||
}
|
||||
|
||||
@@ -162,6 +187,10 @@ void InstrumentMidiIOView::modelChanged()
|
||||
m_fixedOutputNoteSpinBox->setModel( &mp->m_fixedOutputNoteModel );
|
||||
m_outputProgramSpinBox->setModel( &mp->m_outputProgramModel );
|
||||
|
||||
#ifdef PROVIDE_CUSTOM_BASE_VELOCITY_UI
|
||||
m_baseVelocitySpinBox->setModel( &mp->m_baseVelocityModel );
|
||||
#endif
|
||||
|
||||
if( m_rpBtn )
|
||||
{
|
||||
m_rpBtn->setMenu( mp->m_readablePortsMenu );
|
||||
|
||||
@@ -185,13 +185,6 @@ void InstrumentTrack::processAudioBuffer( sampleFrame* buf, const fpp_t frames,
|
||||
|
||||
float v_scale = (float) getVolume() / DefaultVolume;
|
||||
|
||||
// We play MIDI-based instruments at velocity 63 for volume=100%. In order
|
||||
// to get the same output volume, we need to scale it by 2
|
||||
if( m_instrument->flags().testFlag( Instrument::IsMidiBased ) )
|
||||
{
|
||||
v_scale *= 2;
|
||||
}
|
||||
|
||||
// instruments using instrument-play-handles will call this method
|
||||
// without any knowledge about notes, so they pass NULL for n, which
|
||||
// is no problem for us since we just bypass the envelopes+LFOs
|
||||
@@ -260,7 +253,7 @@ void InstrumentTrack::processInEvent( const MidiEvent& event, const MidiTime& ti
|
||||
// create (timed) note-play-handle
|
||||
NotePlayHandle* nph = new NotePlayHandle( this, time.frames( engine::framesPerTick() ),
|
||||
typeInfo<f_cnt_t>::max() / 2,
|
||||
note( MidiTime(), MidiTime(), event.key(), event.volume() ),
|
||||
note( MidiTime(), MidiTime(), event.key(), event.volume( midiPort()->baseVelocity() ) ),
|
||||
NULL, false, event.channel(),
|
||||
NotePlayHandle::OriginMidiInput );
|
||||
if( engine::mixer()->addPlayHandle( nph ) )
|
||||
@@ -289,7 +282,7 @@ void InstrumentTrack::processInEvent( const MidiEvent& event, const MidiTime& ti
|
||||
{
|
||||
// setVolume() calls processOutEvent() with MidiKeyPressure so the
|
||||
// attached instrument will receive the event as well
|
||||
m_notes[event.key()]->setVolume( event.volume() );
|
||||
m_notes[event.key()]->setVolume( event.volume( midiPort()->baseVelocity() ) );
|
||||
}
|
||||
eventHandled = true;
|
||||
break;
|
||||
|
||||
Reference in New Issue
Block a user