diff --git a/include/AutomatableModel.h b/include/AutomatableModel.h index 206cdafa8..5acc211bc 100644 --- a/include/AutomatableModel.h +++ b/include/AutomatableModel.h @@ -162,7 +162,11 @@ public: { return castValue( m_step ); } - + + //! @brief Returns value scaled with the scale type and min/max values of this model + float scaledValue( float value ) const; + //! @brief Returns value applied with the inverse of this model's scale type + float inverseScaledValue( float value ) const; void setInitValue( const float value ); @@ -187,6 +191,10 @@ public: { setScaleType( setToTrue ? Logarithmic : Linear ); } + bool isScaleLogarithmic() const + { + return m_scaleType == Logarithmic; + } void setStep( const float step ); @@ -224,16 +232,7 @@ public: return "automatablemodel"; } - QString displayValue( const float val ) const - { - switch( m_dataType ) - { - case Float: return QString::number( castValue( val ) ); - case Integer: return QString::number( castValue( val ) ); - case Bool: return QString::number( castValue( val ) ); - } - return "0"; - } + QString displayValue( const float val ) const; bool hasLinkedModels() const { diff --git a/include/lmms_constants.h b/include/lmms_constants.h index 190d963b1..2f83a2300 100644 --- a/include/lmms_constants.h +++ b/include/lmms_constants.h @@ -22,10 +22,11 @@ * */ -#ifndef _LMMS_CONSTANTS_H -#define _LMMS_CONSTANTS_H +#ifndef LMMS_CONSTANTS_H +#define LMMS_CONSTANTS_H const float F_PI = 3.1415926535f; +const float F_E = 2.718281828459045f; const float F_2PI = 2*F_PI; const float F_PI_2 = F_PI*0.5; diff --git a/include/lmms_math.h b/include/lmms_math.h index 94d13b161..6f361c971 100644 --- a/include/lmms_math.h +++ b/include/lmms_math.h @@ -128,5 +128,40 @@ static inline double sinc( double _x ) } +//! @brief Exponential function that deals with negative bases +static inline float signedPowf( float v, float e ) +{ + return v < 0 + ? powf( -v, e ) * -1.0f + : powf( v, e ); +} + + +//! @brief Scales @value from linear to logarithmic. +//! Value should be within [0,1] +static inline float logToLinearScale( float min, float max, float value ) +{ + if( min < 0 ) + { + const float mmax = qMax( qAbs( min ), qAbs( max ) ); + const float val = value * ( max - min ) + min; + return signedPowf( val / mmax, F_E ) * mmax; + } + return powf( value, F_E ) * ( max - min ) + min; +} + + +//! @brief Scales value from logarithmic to linear. Value should be in min-max range. +static inline float linearToLogScale( float min, float max, float value ) +{ + static const float EXP = 1.0f / F_E; + const float val = ( value - min ) / ( max - min ); + if( min < 0 ) + { + const float mmax = qMax( qAbs( min ), qAbs( max ) ); + return signedPowf( value / mmax, EXP ) * mmax; + } + return powf( val, EXP ) * ( max - min ) + min; +} #endif diff --git a/plugins/LadspaEffect/LadspaControlDialog.h b/plugins/LadspaEffect/LadspaControlDialog.h index 204c7ce6d..ba684b852 100644 --- a/plugins/LadspaEffect/LadspaControlDialog.h +++ b/plugins/LadspaEffect/LadspaControlDialog.h @@ -24,8 +24,8 @@ * */ -#ifndef _LADSPA_CONTROL_DIALOG_H -#define _LADSPA_CONTROL_DIALOG_H +#ifndef LADSPA_CONTROL_DIALOG_H +#define LADSPA_CONTROL_DIALOG_H #include "EffectControlDialog.h" diff --git a/plugins/LadspaEffect/LadspaControls.h b/plugins/LadspaEffect/LadspaControls.h index a606f3a6c..47b359b86 100644 --- a/plugins/LadspaEffect/LadspaControls.h +++ b/plugins/LadspaEffect/LadspaControls.h @@ -22,8 +22,8 @@ * */ -#ifndef _LADSPA_CONTROLS_H -#define _LADSPA_CONTROLS_H +#ifndef LADSPA_CONTROLS_H +#define LADSPA_CONTROLS_H #include "EffectControls.h" #include "LadspaControl.h" diff --git a/src/core/AutomatableModel.cpp b/src/core/AutomatableModel.cpp index e8e22e5c2..00f2b4d5f 100644 --- a/src/core/AutomatableModel.cpp +++ b/src/core/AutomatableModel.cpp @@ -27,7 +27,7 @@ #include "AutomatableModel.h" #include "AutomationPattern.h" #include "ControllerConnection.h" - +#include "lmms_math.h" float AutomatableModel::s_copiedValue = 0; @@ -246,25 +246,42 @@ void AutomatableModel::setValue( const float value ) -//! @brief Scales @value from linear to logarithmic. -//! Value should be within [0,1] -//! @todo This should be moved into a maths header -template T logToLinearScale(T min, T max, T value) +template T AutomatableModel::logToLinearScale( T value ) const { - return exp( ( log(max) - log(min) ) * value + log(min) ); + return castValue( ::logToLinearScale( minValue(), maxValue(), static_cast( value ) ) ); +} + + +float AutomatableModel::scaledValue( float value ) const +{ + return m_scaleType == Linear + ? value + : logToLinearScale( ( value - minValue() ) / m_range ); +} + + +float AutomatableModel::inverseScaledValue( float value ) const +{ + return m_scaleType == Linear + ? value + : ::linearToLogScale( minValue(), maxValue(), value ); } - -template T AutomatableModel::logToLinearScale(T value) const +QString AutomatableModel::displayValue( const float val ) const { - return ::logToLinearScale( minValue(), maxValue(), value ); + switch( m_dataType ) + { + case Float: return QString::number( castValue( scaledValue( val ) ) ); + case Integer: return QString::number( castValue( scaledValue( val ) ) ); + case Bool: return QString::number( castValue( scaledValue( val ) ) ); + } + return "0"; } - //! @todo: this should be moved into a maths header template void roundAt( T& value, const T& where, const T& step_size ) @@ -293,13 +310,7 @@ void AutomatableModel::setAutomatedValue( const float value ) ++m_setValueDepth; const float oldValue = m_value; - const float scaled_value = - ( m_scaleType == Linear ) - ? value - : logToLinearScale( - // fits value into [0,1]: - (value - minValue()) / maxValue() - ); + const float scaled_value = scaledValue( value ); m_value = fittedValue( scaled_value ); @@ -313,9 +324,7 @@ void AutomatableModel::setAutomatedValue( const float value ) !(*it)->fittedValue( m_value ) != (*it)->m_value ) { - // @TOBY: don't take m_value, but better: value, - // otherwise, we convert to log twice? - (*it)->setAutomatedValue( m_value ); + (*it)->setAutomatedValue( value ); } } emit dataChanged(); @@ -604,13 +613,7 @@ float AutomatableModel::globalAutomationValueAt( const MidiTime& time ) { // scale/fit the value appropriately and return it const float value = latestPattern->valueAt( time - latestPattern->startPosition() ); - const float scaled_value = - ( m_scaleType == Linear ) - ? value - : logToLinearScale( - // fits value into [0,1]: - (value - minValue()) / maxValue() - ); + const float scaled_value = scaledValue( value ); return fittedValue( scaled_value ); } // if we still find no pattern, the value at that time is undefined so diff --git a/src/core/LadspaControl.cpp b/src/core/LadspaControl.cpp index 8d31f55d6..3bee1c2cb 100644 --- a/src/core/LadspaControl.cpp +++ b/src/core/LadspaControl.cpp @@ -76,7 +76,7 @@ LadspaControl::LadspaControl( Model * _parent, port_desc_t * _port, ( m_port->max - m_port->min ) / ( m_port->name.toUpper() == "GAIN" && m_port->max == 10.0f ? 4000.0f : - 400.0f ) ); + ( m_port->suggests_logscale ? 8000.0f : 800.0f ) ) ); m_knobModel.setInitValue( m_port->def ); connect( &m_knobModel, SIGNAL( dataChanged() ), this, SLOT( knobChanged() ) ); @@ -87,7 +87,7 @@ LadspaControl::LadspaControl( Model * _parent, port_desc_t * _port, case TIME: m_tempoSyncKnobModel.setRange( m_port->min, m_port->max, ( m_port->max - - m_port->min ) / 400.0f ); + m_port->min ) / 800.0f ); m_tempoSyncKnobModel.setInitValue( m_port->def ); connect( &m_tempoSyncKnobModel, SIGNAL( dataChanged() ), this, SLOT( tempoKnobChanged() ) ); diff --git a/src/gui/AutomationEditor.cpp b/src/gui/AutomationEditor.cpp index 71abe18c1..7e65b0031 100644 --- a/src/gui/AutomationEditor.cpp +++ b/src/gui/AutomationEditor.cpp @@ -1359,7 +1359,8 @@ inline void AutomationEditor::drawCross( QPainter & _p ) QPoint tt_pos = QCursor::pos(); tt_pos.ry() -= 64; tt_pos.rx() += 32; - QToolTip::showText( tt_pos,QString::number( level ),this); + float scaledLevel = m_pattern->firstObject()->scaledValue( level ); + QToolTip::showText( tt_pos, QString::number( scaledLevel ), this ); } @@ -1896,17 +1897,10 @@ float AutomationEditor::getLevel( int _y ) // pressed level float level = roundf( ( m_bottomLevel + ( m_y_auto ? ( m_maxLevel - m_minLevel ) * ( level_line_y - _y ) - / (float)( level_line_y - TOP_MARGIN ) : + / (float)( level_line_y - ( TOP_MARGIN + 2 ) ) : ( level_line_y - _y ) / (float)m_y_delta ) ) / m_step ) * m_step; // some range-checking-stuff - if( level < m_bottomLevel ) - { - level = m_bottomLevel; - } - else if( level > m_topLevel ) - { - level = m_topLevel; - } + level = qBound( m_bottomLevel, level, m_topLevel ); return( level ); } diff --git a/src/gui/widgets/knob.cpp b/src/gui/widgets/knob.cpp index fe33a6b2c..7bb226795 100644 --- a/src/gui/widgets/knob.cpp +++ b/src/gui/widgets/knob.cpp @@ -34,8 +34,8 @@ #ifndef __USE_XOPEN #define __USE_XOPEN #endif -#include +#include "lmms_math.h" #include "knob.h" #include "caption_menu.h" #include "config_mgr.h" @@ -320,7 +320,7 @@ bool knob::updateAngle() int angle = 0; if( model() && model()->maxValue() != model()->minValue() ) { - angle = angleFromValue( model()->value(), model()->minValue(), model()->maxValue(), m_totalAngle ); + angle = angleFromValue( model()->inverseScaledValue( model()->value() ), model()->minValue(), model()->maxValue(), m_totalAngle ); } if( qAbs( angle - m_angle ) > 3 ) { @@ -388,7 +388,7 @@ void knob::drawKnob( QPainter * _p ) p.setRenderHint( QPainter::Antialiasing ); - const int centerAngle = angleFromValue( model()->centerValue(), model()->minValue(), model()->maxValue(), m_totalAngle ); + const int centerAngle = angleFromValue( model()->inverseScaledValue( model()->centerValue() ), model()->minValue(), model()->maxValue(), m_totalAngle ); const int arcLineWidth = 2; const int arcRectSize = m_knobPixmap->width() - arcLineWidth; @@ -669,15 +669,39 @@ void knob::setPosition( const QPoint & _p ) { const float value = getValue( _p ) + m_leftOver; const float step = model()->step(); - - if( qAbs( value ) >= step ) + const float oldValue = model()->value(); + + + if( model()->isScaleLogarithmic() ) // logarithmic code { - model()->setValue( model()->value() - value ); - m_leftOver = 0.0f; + const float pos = model()->minValue() < 0 + ? oldValue / qMax( qAbs( model()->maxValue() ), qAbs( model()->minValue() ) ) + : ( oldValue - model()->minValue() ) / model()->range(); + const float ratio = 0.1f + qAbs( pos ) * 15.f; + float newValue = value * ratio; + if( qAbs( newValue ) >= step ) + { + model()->setValue( oldValue - newValue ); + m_leftOver = 0.0f; + } + else + { + m_leftOver = value; + } } - else + + + else // linear code { - m_leftOver = value; + if( qAbs( value ) >= step ) + { + model()->setValue( oldValue - value ); + m_leftOver = 0.0f; + } + else + { + m_leftOver = value; + } } }