Merge pull request #788 from diizy/models

Models branch for 1.2
This commit is contained in:
Tobias Doerffel
2014-05-31 15:12:33 +02:00
18 changed files with 523 additions and 132 deletions

View File

@@ -30,6 +30,7 @@
#include "JournallingObject.h"
#include "Model.h"
#include "MidiTime.h"
#include "ValueBuffer.h"
// simple way to map a property of a view to a model
@@ -98,6 +99,13 @@ public:
}
bool isAutomated() const;
bool isAutomatedOrControlled() const
{
return isAutomated() || m_controllerConnection != NULL;
}
bool hasSampleExactData() const;
ControllerConnection* controllerConnection() const
{
@@ -134,6 +142,12 @@ public:
float controllerValue( int frameOffset ) const;
// returns sample-exact data as a ValueBuffer
// should only be called when sample-exact data exists
// in other cases (eg. for automation), the receiving end should interpolate
// the values themselves
ValueBuffer * valueBuffer();
template<class T>
T initValue() const
{
@@ -241,6 +255,16 @@ public:
}
float globalAutomationValueAt( const MidiTime& time );
bool hasStrictStepSize() const
{
return m_hasStrictStepSize;
}
void setStrictStepSize( const bool b )
{
m_hasStrictStepSize = b;
}
public slots:
virtual void reset();
@@ -254,7 +278,7 @@ protected:
//! max() and aligned according to the step size (step size 0.05 -> value
//! 0.12345 becomes 0.10 etc.). You should always call it at the end after
//! doing your own calculations.
float fittedValue( float value ) const;
float fittedValue( float value, bool forceStep = false ) const;
private:
@@ -290,10 +314,13 @@ private:
float m_range;
float m_centerValue;
// most objects will need this temporarily (until sampleExact is
// standard)
// currently unused?
float m_oldValue;
int m_setValueDepth;
// used to determine if step size should be applied strictly (ie. always)
// or only when value set from gui (default)
bool m_hasStrictStepSize;
AutoModelVector m_linkedModels;
bool m_hasLinkedModels;
@@ -305,6 +332,7 @@ private:
static float s_copiedValue;
ValueBuffer m_valueBuffer;
signals:
void initValueChanged( float val );

View File

@@ -31,6 +31,7 @@
#include "Mixer.h"
#include "Model.h"
#include "JournallingObject.h"
#include "ValueBuffer.h"
class ControllerDialog;
class Controller;
@@ -62,6 +63,8 @@ public:
virtual ~Controller();
virtual float currentValue( int _offset );
// The per-controller get-value-in-buffers function
virtual ValueBuffer * valueBuffer();
inline bool isSampleExact() const
{
@@ -111,6 +114,10 @@ public:
return tLimit<float>( _val, 0.0f, 1.0f );
}
static long runningPeriods()
{
return s_periods;
}
static unsigned int runningFrames();
static float runningTime();
@@ -138,6 +145,15 @@ protected:
// The internal per-controller get-value function
virtual float value( int _offset );
virtual void updateValueBuffer();
// buffer for storing sample-exact values in case there
// are more than one model wanting it, so we don't have to create it
// again every time
ValueBuffer m_valueBuffer;
// when we last updated the valuebuffer - so we know if we have to update it
long m_bufferLastUpdated;
float m_currentValue;
bool m_sampleExact;
int m_connectionCount;
@@ -147,7 +163,7 @@ protected:
static ControllerVector s_controllers;
static unsigned int s_frames;
static long s_periods;
signals:

View File

@@ -27,14 +27,15 @@
*/
#ifndef _CONTROLLER_CONNECTION_H
#define _CONTROLLER_CONNECTION_H
#ifndef CONTROLLER_CONNECTION_H
#define CONTROLLER_CONNECTION_H
#include <QtCore/QObject>
#include <QtCore/QVector>
#include "Controller.h"
#include "JournallingObject.h"
#include "ValueBuffer.h"
class ControllerConnection;
@@ -64,6 +65,11 @@ public:
{
return m_controller->currentValue( _offset );
}
ValueBuffer * valueBuffer()
{
return m_controller->valueBuffer();
}
inline void setTargetName( const QString & _name );

View File

@@ -22,8 +22,8 @@
*
*/
#ifndef _LFO_CONTROLLER_H
#define _LFO_CONTROLLER_H
#ifndef LFO_CONTROLLER_H
#define LFO_CONTROLLER_H
#include <QtGui/QWidget>
@@ -59,8 +59,8 @@ public slots:
protected:
// The internal per-controller get-value function
virtual float value( int _offset );
// The internal per-controller value updating function
virtual void updateValueBuffer();
FloatModel m_baseModel;
TempoSyncKnobModel m_speedModel;
@@ -69,17 +69,19 @@ protected:
IntModel m_waveModel;
IntModel m_multiplierModel;
int m_duration;
int m_phaseCorrection;
int m_phaseOffset;
float m_duration;
float m_phaseOffset;
float m_currentPhase;
sample_t (*m_sampleFunction)( const float );
private:
SampleBuffer * m_userDefSampleBuffer;
protected slots:
void updatePhase();
void updateSampleFunction();
void updateDuration();
friend class LfoControllerDialog;

View File

@@ -22,8 +22,8 @@
*
*/
#ifndef _MIDI_CONTROLLER_H
#define _MIDI_CONTROLLER_H
#ifndef MIDI_CONTROLLER_H
#define MIDI_CONTROLLER_H
#include <QtGui/QWidget>
@@ -67,13 +67,14 @@ public slots:
protected:
// The internal per-controller get-value function
virtual float value( int _offset );
virtual void updateValueBuffer();
MidiPort m_midiPort;
float m_lastValue;
float m_previousValue;
friend class ControllerConnectionDialog;
friend class AutoDetectMidiController;

View File

@@ -22,8 +22,8 @@
*
*/
#ifndef _PEAK_CONTROLLER_H
#define _PEAK_CONTROLLER_H
#ifndef PEAK_CONTROLLER_H
#define PEAK_CONTROLLER_H
#include <QtGui/QWidget>
@@ -65,7 +65,7 @@ public slots:
protected:
// The internal per-controller get-value function
virtual float value( int _offset );
virtual void updateValueBuffer();
PeakControllerEffect * m_peakEffect;

137
include/ValueBuffer.h Normal file
View File

@@ -0,0 +1,137 @@
/*
* ValueBuffer.h - a container class for passing buffers of model values around
*
* Copyright (c) 2014 Vesa Kivimäki <contact/dot/diizy/at/nbl/dot/fi>
* Copyright (c) 2008-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
*
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program (see COPYING); if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301 USA.
*
*/
#ifndef VALUE_BUFFER_H
#define VALUE_BUFFER_H
#include "interpolation.h"
#include <string.h>
class ValueBuffer
{
public:
ValueBuffer()
{
m_values = NULL;
m_length = 0;
}
ValueBuffer( int length )
{
m_values = new float[length];
m_length = length;
}
ValueBuffer( float * values, int length )
{
m_values = new float[length];
m_length = length;
memcpy( m_values, values, sizeof(float) * length );
}
ValueBuffer( float value, int length )
{
m_values = new float[length];
m_length = length;
for( int i = 0; i < length; i++ )
{
m_values[i] = value;
}
}
virtual ~ValueBuffer()
{
delete[] m_values;
}
void clear()
{
delete[] m_values;
m_values = NULL;
m_length = 0;
}
void fill( float value )
{
for( int i = 0; i < m_length; i++ )
{
m_values[i] = value;
}
}
float value( int offset ) const
{
return m_values[ offset % m_length ];
}
void setValue( int offset, float value )
{
m_values[ offset % m_length ] = value;
}
float * values() const
{
return m_values;
}
void setValues( float * values )
{
m_values = values;
}
int length() const
{
return m_length;
}
void setLength( const int length )
{
m_length = length;
}
void interpolate( float start, float end )
{
float f = 0.0f;
const float fstep = 1.0f / static_cast<float>( m_length );
for( int i = 0; i < m_length; i++ )
{
f += fstep;
m_values[i] = linearInterpolate( start, end, f );
}
}
static ValueBuffer interpolatedBuffer( float start, float end, int length )
{
ValueBuffer vb = ValueBuffer( length );
vb.interpolate( start, end );
return vb;
}
private:
float * m_values;
int m_length;
};
#endif

View File

@@ -22,8 +22,8 @@
*
*/
#ifndef _SONG_H
#define _SONG_H
#ifndef SONG_H
#define SONG_H
#include <QtCore/QSharedMemory>
#include <QtCore/QVector>
@@ -131,6 +131,10 @@ public:
{
return currentTick();
}
inline f_cnt_t getFrames() const
{
return currentFrame();
}
inline bool isTempoAutomated()
{
return m_tempoModel.isAutomated();
@@ -310,6 +314,12 @@ private:
{
return m_playPos[m_playMode].getTicks();
}
inline f_cnt_t currentFrame() const
{
return m_playPos[m_playMode].getTicks() * engine::framesPerTick() + m_playPos[m_playMode].currentFrame();
}
void setPlayPos( tick_t _ticks, PlayModes _play_mode );
void saveControllerStates( QDomDocument & _doc, QDomElement & _this );
@@ -362,6 +372,7 @@ private:
signals:
void projectLoaded();
void playbackStateChanged();
void playbackPositionChanged();
void lengthChanged( int _tacts );
void tempoChanged( bpm_t _new_bpm );
void timeSignatureChanged( int _old_ticks_per_tact,

View File

@@ -74,29 +74,62 @@ bool AmplifierEffect::processAudioBuffer( sampleFrame* buf, const fpp_t frames )
double outSum = 0.0;
const float d = dryLevel();
const float w = wetLevel();
ValueBuffer * volBuf = m_ampControls.m_volumeModel.hasSampleExactData()
? m_ampControls.m_volumeModel.valueBuffer()
: NULL;
ValueBuffer * panBuf = m_ampControls.m_panModel.hasSampleExactData()
? m_ampControls.m_panModel.valueBuffer()
: NULL;
ValueBuffer * leftBuf = m_ampControls.m_leftModel.hasSampleExactData()
? m_ampControls.m_leftModel.valueBuffer()
: NULL;
ValueBuffer * rightBuf = m_ampControls.m_rightModel.hasSampleExactData()
? m_ampControls.m_rightModel.valueBuffer()
: NULL;
for( fpp_t f = 0; f < frames; ++f )
{
// qDebug( "offset %d, value %f", f, m_ampControls.m_volumeModel.value( f ) );
sample_t s[2] = { buf[f][0], buf[f][1] };
// convert vol/pan values to left/right values
const float left1 = m_ampControls.m_volumeModel.value( f ) *
( m_ampControls.m_panModel.value( f ) <= 0
? 1.0
: 1.0 - m_ampControls.m_panModel.value( f ) / 100.0 );
const float right1 = m_ampControls.m_volumeModel.value( f ) *
( m_ampControls.m_panModel.value( f ) >= 0
? 1.0
: 1.0 + m_ampControls.m_panModel.value( f ) / 100.0 );
// vol knob
if( volBuf )
{
s[0] *= volBuf->values()[ f ] * 0.01f;
s[1] *= volBuf->values()[ f ] * 0.01f;
}
else
{
s[0] *= m_ampControls.m_volumeModel.value() * 0.01f;
s[1] *= m_ampControls.m_volumeModel.value() * 0.01f;
}
// first stage amplification
s[0] *= ( left1 / 100.0 );
s[1] *= ( right1 / 100.0 );
// convert pan values to left/right values
const float pan = panBuf
? panBuf->values()[ f ]
: m_ampControls.m_panModel.value();
const float left1 = pan <= 0
? 1.0
: 1.0 - m_ampControls.m_panModel.value( f ) * 0.01f;
const float right1 = pan >= 0
? 1.0
: 1.0 + m_ampControls.m_panModel.value( ) * 0.01f;
// second stage amplification
s[0] *= ( m_ampControls.m_leftModel.value( f ) / 100.0 );
s[1] *= ( m_ampControls.m_rightModel.value( f ) / 100.0 );
const float left2 = leftBuf
? leftBuf->values()[ f ]
: m_ampControls.m_leftModel.value();
const float right2 = rightBuf
? rightBuf->values()[ f ]
: m_ampControls.m_rightModel.value();
s[0] *= left1 * left2 * 0.01;
s[1] *= right1 * right2 * 0.01;
buf[f][0] = d * buf[f][0] + w * s[0];
buf[f][1] = d * buf[f][1] + w * s[1];

View File

@@ -29,7 +29,7 @@
#include "Effect.h"
#include "AmplifierControls.h"
#include "ValueBuffer.h"
class AmplifierEffect : public Effect
{

View File

@@ -63,6 +63,7 @@ PeakControllerEffect::PeakControllerEffect(
m_effectId( rand() ),
m_peakControls( this ),
m_lastSample( 0 ),
m_previousSample( 0 ),
m_lastRMS( -1 ),
m_lastRMSavail(false),
m_autoController( NULL )
@@ -89,11 +90,15 @@ namespace helpers
{
//! returns 1.0f if val > 0.0f, -1.0 else
inline float sign(float val) { return -1.0f + 2.0f * (val > 0.0f); }
inline float sign( float val )
{
return -1.0f + 2.0f * ( val > 0.0f );
}
//! if val >= 0.0f, returns sqrtf(val), else: -sqrtf(-val)
inline float sqrt_neg(float val) {
return sqrtf(fabs(val)) * helpers::sign(val);
inline float sqrt_neg( float val )
{
return sqrtf( fabs( val ) ) * helpers::sign( val );
}
}
@@ -113,7 +118,7 @@ bool PeakControllerEffect::processAudioBuffer( sampleFrame * _buf,
// RMS:
double sum = 0;
if(c.m_absModel.value())
if( c.m_absModel.value() )
{
for( int i = 0; i < _frames; ++i )
{
@@ -127,8 +132,8 @@ bool PeakControllerEffect::processAudioBuffer( sampleFrame * _buf,
{
// the value is absolute because of squaring,
// so we need to correct it
sum += _buf[i][0]*_buf[i][0]*helpers::sign(_buf[i][0])
+ _buf[i][1]*_buf[i][1]*helpers::sign(_buf[i][1]);
sum += _buf[i][0] * _buf[i][0] * helpers::sign( _buf[i][0] )
+ _buf[i][1] * _buf[i][1] * helpers::sign( _buf[i][1] );
}
}
@@ -157,6 +162,7 @@ bool PeakControllerEffect::processAudioBuffer( sampleFrame * _buf,
curRMS = (1-a)*curRMS + a*m_lastRMS;
const float amount = c.m_amountModel.value() * c.m_amountMultModel.value();
m_previousSample = m_lastSample;
m_lastSample = c.m_baseModel.value() + amount*curRMS;
m_lastRMS = curRMS;

View File

@@ -23,8 +23,8 @@
*/
#ifndef _PEAK_CONTROLLER_EFFECT_H
#define _PEAK_CONTROLLER_EFFECT_H
#ifndef PEAK_CONTROLLER_EFFECT_H
#define PEAK_CONTROLLER_EFFECT_H
#include "Effect.h"
#include "peak_controller_effect_controls.h"
@@ -48,6 +48,11 @@ public:
return m_lastSample;
}
float previousSample()
{
return m_previousSample;
}
PeakController * controller()
{
return m_autoController;
@@ -59,6 +64,7 @@ private:
PeakControllerEffectControls m_peakControls;
float m_lastSample;
float m_previousSample;
float m_lastRMS;
bool m_lastRMSavail;

View File

@@ -48,8 +48,11 @@ AutomatableModel::AutomatableModel( DataType type,
m_range( max - min ),
m_centerValue( m_minValue ),
m_setValueDepth( 0 ),
m_hasStrictStepSize( false ),
m_hasLinkedModels( false ),
m_controllerConnection( NULL )
m_controllerConnection( NULL ),
m_valueBuffer( static_cast<int>( engine::mixer()->framesPerPeriod() ) )
{
setInitValue( val );
}
@@ -69,6 +72,8 @@ AutomatableModel::~AutomatableModel()
{
delete m_controllerConnection;
}
m_valueBuffer.clear();
emit destroyed( id() );
}
@@ -81,7 +86,38 @@ bool AutomatableModel::isAutomated() const
return AutomationPattern::isAutomated( this );
}
bool AutomatableModel::hasSampleExactData() const
{
// if a controller is connected...
if( m_controllerConnection != NULL )
{
// ...and is sample-exact, then return true
if( m_controllerConnection->getController()->isSampleExact() )
{
return true;
}
}
// check also the same for the first linked model
if( hasLinkedModels() )
{
AutomatableModel* lm = m_linkedModels.first();
if( lm->m_controllerConnection != NULL )
{
if( lm->m_controllerConnection->getController()->isSampleExact() )
{
return true;
}
}
}
// if we have values we can interpolate return true
if( m_oldValue != m_value )
{
return true;
}
// otherwise, return false
return false;
}
void AutomatableModel::saveSettings( QDomDocument& doc, QDomElement& element, const QString& name )
@@ -215,10 +251,11 @@ void AutomatableModel::loadSettings( const QDomElement& element, const QString&
void AutomatableModel::setValue( const float value )
{
m_oldValue = m_value;
++m_setValueDepth;
const float old_val = m_value;
m_value = fittedValue( value );
m_value = fittedValue( value, true );
if( old_val != m_value )
{
// add changes to history so user can undo it
@@ -290,10 +327,11 @@ void AutomatableModel::roundAt( T& value, const T& where ) const
void AutomatableModel::setAutomatedValue( const float value )
{
m_oldValue = m_value;
++m_setValueDepth;
const float oldValue = m_value;
const float scaled_value =
const float scaledValue =
( m_scaleType == Linear )
? value
: logToLinearScale(
@@ -301,7 +339,7 @@ void AutomatableModel::setAutomatedValue( const float value )
(value - minValue<float>()) / maxValue<float>()
);
m_value = fittedValue( scaled_value );
m_value = fittedValue( scaledValue );
if( oldValue != m_value )
{
@@ -363,11 +401,11 @@ void AutomatableModel::setStep( const float step )
float AutomatableModel::fittedValue( float value ) const
float AutomatableModel::fittedValue( float value, bool forceStep ) const
{
value = tLimit<float>( value, m_minValue, m_maxValue );
if( m_step != 0 )
if( m_step != 0 && ( m_hasStrictStepSize || forceStep ) )
{
value = nearbyintf( value / m_step ) * m_step;
}
@@ -488,7 +526,7 @@ float AutomatableModel::controllerValue( int frameOffset ) const
"lacks implementation for a scale type");
break;
}
if( typeInfo<float>::isEqual( m_step, 1 ) )
if( typeInfo<float>::isEqual( m_step, 1 ) && m_hasStrictStepSize )
{
return qRound( v );
}
@@ -505,6 +543,67 @@ float AutomatableModel::controllerValue( int frameOffset ) const
}
ValueBuffer * AutomatableModel::valueBuffer()
{
ValueBuffer * vb;
if( m_controllerConnection && m_controllerConnection->getController()->isSampleExact() )
{
vb = m_controllerConnection->valueBuffer();
if( vb )
{
float * values = vb->values();
float * nvalues = m_valueBuffer.values();
switch( m_scaleType )
{
case Linear:
for( int i = 0; i < m_valueBuffer.length(); i++ )
{
nvalues[i] = minValue<float>() + ( range() * values[i] );
}
break;
case Logarithmic:
for( int i = 0; i < m_valueBuffer.length(); i++ )
{
nvalues[i] = logToLinearScale( values[i] );
}
break;
default:
qFatal("AutomatableModel::valueBuffer() "
"lacks implementation for a scale type");
break;
}
return &m_valueBuffer;
}
}
AutomatableModel* lm = NULL;
if( m_hasLinkedModels )
{
lm = m_linkedModels.first();
}
if( lm && lm->controllerConnection() && lm->controllerConnection()->getController()->isSampleExact() )
{
vb = lm->valueBuffer();
float * values = vb->values();
float * nvalues = m_valueBuffer.values();
for( int i = 0; i < vb->length(); i++ )
{
nvalues[i] = fittedValue( values[i], false );
}
return &m_valueBuffer;
}
if( m_oldValue != m_value )
{
m_valueBuffer.interpolate( m_oldValue, m_value );
m_oldValue = m_value;
return &m_valueBuffer;
}
// if we have no sample-exact source for a ValueBuffer, create one and fill it with current value
// ideally, recipients should check first if we hasSampleExactData before fetching ValueBuffers
m_valueBuffer.fill( m_value );
return &m_valueBuffer;
}
void AutomatableModel::unlinkControllerConnection()
@@ -604,14 +703,14 @@ 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 =
const float scaledValue =
( m_scaleType == Linear )
? value
: logToLinearScale(
// fits value into [0,1]:
(value - minValue<float>()) / maxValue<float>()
);
return fittedValue( scaled_value );
return fittedValue( scaledValue );
}
// if we still find no pattern, the value at that time is undefined so
// just return current value as the best we can do

View File

@@ -40,7 +40,7 @@
#include "PeakController.h"
unsigned int Controller::s_frames = 0;
long Controller::s_periods = 0;
QVector<Controller *> Controller::s_controllers;
@@ -49,6 +49,8 @@ Controller::Controller( ControllerTypes _type, Model * _parent,
const QString & _display_name ) :
Model( _parent, _display_name ),
JournallingObject(),
m_valueBuffer( engine::mixer()->framesPerPeriod() ),
m_bufferLastUpdated( -1 ),
m_connectionCount( 0 ),
m_type( _type )
{
@@ -80,6 +82,7 @@ Controller::Controller( ControllerTypes _type, Model * _parent,
}
}
}
updateValueBuffer();
}
@@ -97,6 +100,7 @@ Controller::~Controller()
engine::getSong()->removeController( this );
}
m_valueBuffer.clear();
// Remove connections by destroyed signal
}
@@ -115,17 +119,41 @@ float Controller::currentValue( int _offset )
float Controller::value( int _offset )
float Controller::value( int offset )
{
return 0.5f;
if( m_bufferLastUpdated != s_periods )
{
updateValueBuffer();
}
return m_valueBuffer.values()[ offset ];
}
ValueBuffer * Controller::valueBuffer()
{
if( m_bufferLastUpdated != s_periods )
{
updateValueBuffer();
}
return &m_valueBuffer;
}
void Controller::updateValueBuffer()
{
float * values = m_valueBuffer.values();
for( int i = 0; i < m_valueBuffer.length(); i++ )
{
values[i] = 0.5f;
}
m_bufferLastUpdated = s_periods;
}
// Get position in frames
unsigned int Controller::runningFrames()
{
return s_frames;
return s_periods * engine::mixer()->framesPerPeriod();
}
@@ -133,7 +161,7 @@ unsigned int Controller::runningFrames()
// Get position in seconds
float Controller::runningTime()
{
return s_frames / engine::mixer()->processingSampleRate();
return runningFrames() / engine::mixer()->processingSampleRate();
}
@@ -149,7 +177,7 @@ void Controller::triggerFrameCounter()
emit s_controllers.at(i)->valueChanged();
}
s_frames += engine::mixer()->framesPerPeriod();
s_periods ++;
//emit s_signaler.triggerValueChanged();
}
@@ -157,7 +185,11 @@ void Controller::triggerFrameCounter()
void Controller::resetFrameCounter()
{
s_frames = 0;
for( int i = 0; i < s_controllers.size(); ++i )
{
s_controllers.at( i )->m_bufferLastUpdated = 0;
}
s_periods = 0;
}

View File

@@ -34,6 +34,7 @@
#include "Mixer.h"
#include "LfoController.h"
#include "ControllerDialog.h"
#include "lmms_math.h"
//const float TWO_PI = 6.28318531f;
@@ -47,14 +48,28 @@ LfoController::LfoController( Model * _parent ) :
this, tr( "Oscillator waveform" ) ),
m_multiplierModel( 0, 0, 2, this, tr( "Frequency Multiplier" ) ),
m_duration( 1000 ),
m_phaseCorrection( 0 ),
m_phaseOffset( 0 ),
m_phaseOffset( 0 ),
m_currentPhase( 0 ),
m_sampleFunction( &Oscillator::sinSample ),
m_userDefSampleBuffer( new SampleBuffer )
{
setSampleExact( true );
connect( &m_waveModel, SIGNAL( dataChanged() ),
this, SLOT( updateSampleFunction() ) );
connect( &m_speedModel, SIGNAL( dataChanged() ),
this, SLOT( updateDuration() ) );
connect( &m_multiplierModel, SIGNAL( dataChanged() ),
this, SLOT( updateDuration() ) );
connect( engine::mixer(), SIGNAL( sampleRateChanged() ),
this, SLOT( updateDuration() ) );
connect( engine::getSong(), SIGNAL( playbackStateChanged() ),
this, SLOT( updatePhase() ) );
connect( engine::getSong(), SIGNAL( playbackPositionChanged() ),
this, SLOT( updatePhase() ) );
updateDuration();
}
@@ -72,84 +87,65 @@ LfoController::~LfoController()
}
// This code took forever to get right. It can
// definately be optimized.
// The code should probably be integrated with the oscillator class. But I
// don't know how to use oscillator because it is so confusing
float LfoController::value( int _offset )
void LfoController::updateValueBuffer()
{
int frame = runningFrames() + _offset + m_phaseCorrection;
m_phaseOffset = m_phaseModel.value() / 360.0;
float * values = m_valueBuffer.values();
float phase = m_currentPhase + m_phaseOffset;
//If the song is playing, sync the value with the time of the song.
if( engine::getSong()->isPlaying() || engine::getSong()->isExporting() )
// roll phase up until we're in sync with period counter
m_bufferLastUpdated++;
if( m_bufferLastUpdated < s_periods )
{
// The new duration in frames
// (Samples/Second) / (periods/second) = (Samples/cycle)
float newDurationF =
engine::mixer()->processingSampleRate() *
m_speedModel.value();
switch(m_multiplierModel.value() )
{
case 1:
newDurationF /= 100.0;
break;
case 2:
newDurationF *= 100.0;
break;
default:
break;
}
m_phaseOffset = qRound(
m_phaseModel.value() * newDurationF / 360.0 );
int newDuration = static_cast<int>( newDurationF );
m_phaseCorrection = static_cast<int>(engine::getSong()->getTicks()*engine::framesPerTick())%newDuration
+ m_phaseOffset;
// re-run the first calculation again
frame = m_phaseCorrection + _offset;
int diff = s_periods - m_bufferLastUpdated;
phase += static_cast<float>( engine::framesPerTick() * diff ) / m_duration;
m_bufferLastUpdated += diff;
}
// speedModel 0..1 fast..slow 0ms..20000ms
// duration m_duration
//
// frames / (20seconds of frames)
float sampleFrame = float( frame+m_phaseOffset ) /
(engine::mixer()->processingSampleRate() * m_speedModel.value() );
for( int i = 0; i < m_valueBuffer.length(); i++ )
{
const float currentSample = m_sampleFunction != NULL
? m_sampleFunction( phase )
: m_userDefSampleBuffer->userWaveSample( phase );
values[i] = m_baseModel.value() + ( m_amountModel.value() * currentSample / 2.0f );
phase += 1.0 / m_duration;
}
m_currentPhase = absFraction( phase - m_phaseOffset );
}
void LfoController::updatePhase()
{
m_currentPhase = ( engine::getSong()->getFrames() ) / m_duration;
m_bufferLastUpdated = s_periods - 1;
}
void LfoController::updateDuration()
{
float newDurationF = engine::mixer()->processingSampleRate() * m_speedModel.value();
switch(m_multiplierModel.value() )
{
case 1:
sampleFrame *= 100.0;
newDurationF /= 100.0;
break;
case 2:
sampleFrame /= 100.0;
newDurationF *= 100.0;
break;
default:
break;
}
// 44100 frames/sec
return m_baseModel.value() + ( m_amountModel.value() *
( m_sampleFunction != NULL ?
m_sampleFunction(sampleFrame):
m_userDefSampleBuffer->userWaveSample(sampleFrame) )
/ 2.0f );
m_duration = newDurationF;
}
void LfoController::updateSampleFunction()
{
switch( m_waveModel.value() )

View File

@@ -51,6 +51,7 @@ PeakController::PeakController( Model * _parent,
Controller( Controller::PeakController, _parent, tr( "Peak Controller" ) ),
m_peakEffect( _peak_effect )
{
setSampleExact( true );
if( m_peakEffect )
{
connect( m_peakEffect, SIGNAL( destroyed( ) ),
@@ -74,18 +75,20 @@ PeakController::~PeakController()
}
float PeakController::value( int _offset )
void PeakController::updateValueBuffer()
{
if( m_peakEffect )
{
return m_peakEffect->lastSample();
m_valueBuffer.interpolate( m_peakEffect->previousSample(), m_peakEffect->lastSample() );
}
return( 0 );
else
{
m_valueBuffer.fill( 0 );
}
m_bufferLastUpdated = s_periods;
}
void PeakController::handleDestroyedEffect( )
{
// possible race condition...

View File

@@ -41,6 +41,7 @@ MidiController::MidiController( Model * _parent ) :
engine::mixer()->midiClient(), this, this, MidiPort::Input ),
m_lastValue( 0.0f )
{
setSampleExact( true );
connect( &m_midiPort, SIGNAL( modeChanged() ),
this, SLOT( updateName() ) );
}
@@ -55,14 +56,21 @@ MidiController::~MidiController()
float MidiController::value( int _offset )
void MidiController::updateValueBuffer()
{
return m_lastValue;
if( m_previousValue != m_lastValue )
{
m_valueBuffer.interpolate( m_previousValue, m_lastValue );
m_previousValue = m_lastValue;
}
else
{
m_valueBuffer.fill( m_lastValue );
}
m_bufferLastUpdated = s_periods;
}
void MidiController::updateName()
{
setName( QString("MIDI ch%1 ctrl%2").
@@ -86,6 +94,7 @@ void MidiController::processInEvent( const MidiEvent& event, const MidiTime& tim
m_midiPort.inputChannel() == 0 ) )
{
unsigned char val = event.controllerValue();
m_previousValue = m_lastValue;
m_lastValue = (float)( val ) / 127.0f;
emit valueChanged();
}

View File

@@ -523,6 +523,12 @@ void song::setPlayPos( tick_t _ticks, PlayModes _play_mode )
m_elapsedMilliSeconds += (((( _ticks - m_playPos[_play_mode].getTicks()))*60*1000/48)/getTempo());
m_playPos[_play_mode].setTicks( _ticks );
m_playPos[_play_mode].setCurrentFrame( 0.0f );
// send a signal if playposition changes during playback
if( isPlaying() )
{
emit playbackPositionChanged();
}
}