code:
/*
 * EffectChain.cpp - class for processing and effects chain
 *
 * Copyright (c) 2006-2008 Danny McRae <khjklujn/at/users.sourceforge.net>
 * Copyright (c) 2008-2009 Tobias Doerffel <tobydox/at/users.sourceforge.net>
 *
 * This file is part of LMMS - https://lmms.io
 *
 * 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.
 *
 */

#include <QDomElement>

#include "EffectChain.h"
#include "Effect.h"
#include "DummyEffect.h"
#include "MixHelpers.h"
#include "Song.h"

EffectChain::EffectChain( Model * _parent ) :
	Model( _parent ),
	SerializingObject(),
	m_enabledModel( false, NULL, tr( "Effects enabled" ) )
{
}

EffectChain::~EffectChain()
{
	clear();
}

void EffectChain::saveSettings( QDomDocument & _doc, QDomElement & _this )
{
	m_enabledModel.saveSettings( _doc, _this, "enabled" );
	_this.setAttribute( "numofeffects", m_effects.count() );

	for( Effect* effect : m_effects)
	{
		if( DummyEffect* dummy = dynamic_cast<DummyEffect*>(effect) )
		{
			_this.appendChild( dummy->originalPluginData() );
		}
		else
		{
			QDomElement ef = effect->saveState( _doc, _this );
			ef.setAttribute( "name", QString::fromUtf8( effect->descriptor()->name ) );
			ef.appendChild( effect->key().saveXML( _doc ) );
		}
	}
}

void EffectChain::loadSettings( const QDomElement & _this )
{
	clear();

	// TODO This method should probably also lock the mixer

	m_enabledModel.loadSettings( _this, "enabled" );

	const int plugin_cnt = _this.attribute( "numofeffects" ).toInt();

	QDomNode node = _this.firstChild();
	int fx_loaded = 0;
	while( !node.isNull() && fx_loaded < plugin_cnt )
	{
		if( node.isElement() && node.nodeName() == "effect" )
		{
			QDomElement effectData = node.toElement();

			const QString name = effectData.attribute( "name" );
			EffectKey key( effectData.elementsByTagName( "key" ).item( 0 ).toElement() );

			Effect* e = Effect::instantiate( name.toUtf8(), this, &key );

			if( e != NULL && e->isOkay() && e->nodeName() == node.nodeName() )
			{
				e->restoreState( effectData );
			}
			else
			{
				delete e;
				e = new DummyEffect( parentModel(), effectData );
			}

			m_effects.push_back( e );
			++fx_loaded;
		}
		node = node.nextSibling();
	}

	emit dataChanged();
}

void EffectChain::appendEffect( Effect * _effect )
{
	Engine::mixer()->requestChangeInModel();
	m_effects.append( _effect );
	Engine::mixer()->doneChangeInModel();

	m_enabledModel.setValue( true );

	emit dataChanged();
}

void EffectChain::removeEffect( Effect * _effect )
{
	Engine::mixer()->requestChangeInModel();

	Effect ** found = std::find( m_effects.begin(), m_effects.end(), _effect );
	if( found == m_effects.end() )
	{
		Engine::mixer()->doneChangeInModel();
		return;
	}
	m_effects.erase( found );

	Engine::mixer()->doneChangeInModel();

	if( m_effects.isEmpty() )
	{
		m_enabledModel.setValue( false );
	}

	emit dataChanged();
}

void EffectChain::moveDown( Effect * _effect )
{
	if( _effect != m_effects.last() )
	{
		int i = m_effects.indexOf(_effect);
		std::swap(m_effects[i + 1], m_effects[i]);
	}
}

void EffectChain::moveUp( Effect * _effect )
{
	if( _effect != m_effects.first() )
	{
		int i = m_effects.indexOf(_effect);
		std::swap(m_effects[i - 1], m_effects[i]);
	}
}

bool EffectChain::processAudioBuffer( sampleFrame * _buf, const fpp_t _frames, bool hasInputNoise )
{
	if( m_enabledModel.value() == false )
	{
		return false;
	}

	MixHelpers::sanitize( _buf, _frames );

	bool moreEffects = false;
	for( EffectList::Iterator it = m_effects.begin(); it != m_effects.end(); ++it )
	{
		if( hasInputNoise || ( *it )->isRunning() )
		{
			moreEffects |= ( *it )->processAudioBuffer( _buf, _frames );
			MixHelpers::sanitize( _buf, _frames );
		}
	}

	return moreEffects;
}

void EffectChain::startRunning()
{
	if( m_enabledModel.value() == false )
	{
		return;
	}

	for( EffectList::Iterator it = m_effects.begin();
						it != m_effects.end(); it++ )
	{
		( *it )->startRunning();
	}
}

void EffectChain::clear()
{
	emit aboutToClear();

	Engine::mixer()->requestChangeInModel();

	while( m_effects.count() )
	{
		Effect * e = m_effects[m_effects.count() - 1];
		m_effects.pop_back();
		delete e;
	}

	Engine::mixer()->doneChangeInModel();

	m_enabledModel.setValue( false );
}


code:
/*
 * Engine.cpp - implementation of LMMS' engine-system
 *
 * Copyright (c) 2006-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
 *
 * This file is part of LMMS - https://lmms.io
 *
 * 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.
 *
 */

#include "Engine.h"
#include "BBTrackContainer.h"
#include "ConfigManager.h"
#include "FxMixer.h"
#include "Ladspa2LMMS.h"
#include "Lv2Manager.h"
#include "Mixer.h"
#include "Plugin.h"
#include "PresetPreviewPlayHandle.h"
#include "ProjectJournal.h"
#include "Song.h"
#include "BandLimitedWave.h"

float LmmsCore::s_framesPerTick;
Mixer* LmmsCore::s_mixer = NULL;
FxMixer * LmmsCore::s_fxMixer = NULL;
BBTrackContainer * LmmsCore::s_bbTrackContainer = NULL;
Song * LmmsCore::s_song = NULL;
ProjectJournal * LmmsCore::s_projectJournal = NULL;
#ifdef LMMS_HAVE_LV2
Lv2Manager * LmmsCore::s_lv2Manager = nullptr;
#endif
Ladspa2LMMS * LmmsCore::s_ladspaManager = NULL;
void* LmmsCore::s_dndPluginKey = nullptr;

void LmmsCore::init( bool renderOnly )
{
	LmmsCore *engine = inst();

	emit engine->initProgress(tr("Generating wavetables"));
	// generate (load from file) bandlimited wavetables
	BandLimitedWave::generateWaves();

	emit engine->initProgress(tr("Initializing data structures"));
	s_projectJournal = new ProjectJournal;
	s_mixer = new Mixer( renderOnly );
	s_song = new Song;
	s_fxMixer = new FxMixer;
	s_bbTrackContainer = new BBTrackContainer;

#ifdef LMMS_HAVE_LV2
	s_lv2Manager = new Lv2Manager;
	s_lv2Manager->initPlugins();
#endif
	s_ladspaManager = new Ladspa2LMMS;

	s_projectJournal->setJournalling( true );

	emit engine->initProgress(tr("Opening audio and midi devices"));
	s_mixer->initDevices();

	PresetPreviewPlayHandle::init();

	emit engine->initProgress(tr("Launching mixer threads"));
	s_mixer->startProcessing();
}

void LmmsCore::destroy()
{
	s_projectJournal->stopAllJournalling();
	s_mixer->stopProcessing();

	PresetPreviewPlayHandle::cleanup();

	s_song->clearProject();

	deleteHelper( &s_bbTrackContainer );

	deleteHelper( &s_fxMixer );
	deleteHelper( &s_mixer );

#ifdef LMMS_HAVE_LV2
	deleteHelper( &s_lv2Manager );
#endif
	deleteHelper( &s_ladspaManager );

	//delete ConfigManager::inst();
	deleteHelper( &s_projectJournal );

	deleteHelper( &s_song );

	delete ConfigManager::inst();
}

bool LmmsCore::ignorePluginBlacklist()
{
	const char* envVar = getenv("LMMS_IGNORE_BLACKLIST");
	return (envVar && *envVar);
}

float LmmsCore::framesPerTick(sample_rate_t sampleRate)
{
	return sampleRate * 60.0f * 4 /
			DefaultTicksPerBar / s_song->getTempo();
}

void LmmsCore::updateFramesPerTick()
{
	s_framesPerTick = s_mixer->processingSampleRate() * 60.0f * 4 /
				DefaultTicksPerBar / s_song->getTempo();
}

void LmmsCore::setDndPluginKey(void *newKey)
{
	Q_ASSERT(static_cast<Plugin::Descriptor::SubPluginFeatures::Key*>(newKey));
	s_dndPluginKey = newKey;
}

void *LmmsCore::pickDndPluginKey()
{
	return s_dndPluginKey;
}

LmmsCore * LmmsCore::s_instanceOfMe = NULL;

repo_name: CYBERDEViL/LMMS

license: mit


code:
/*
 * EnvelopeAndLfoParameters.cpp - class EnvelopeAndLfoParameters
 *
 * Copyright (c) 2004-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
 *
 * This file is part of LMMS - https://lmms.io
 *
 * 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.
 *
 */

#include <QDomElement>

#include "EnvelopeAndLfoParameters.h"
#include "Engine.h"
#include "Mixer.h"
#include "Oscillator.h"

// how long should be each envelope-segment maximal (e.g. attack)?
extern const float SECS_PER_ENV_SEGMENT = 5.0f;
// how long should be one LFO-oscillation maximal?
extern const float SECS_PER_LFO_OSCILLATION = 20.0f;
// minimum number of frames for ENV/LFO stages that mustn't be '0'
const f_cnt_t minimumFrames = 1;

EnvelopeAndLfoParameters::LfoInstances * EnvelopeAndLfoParameters::s_lfoInstances = NULL;

void EnvelopeAndLfoParameters::LfoInstances::trigger()
{
	QMutexLocker m( &m_lfoListMutex );
	for( LfoList::Iterator it = m_lfos.begin();
							it != m_lfos.end(); ++it )
	{
		( *it )->m_lfoFrame +=
				Engine::mixer()->framesPerPeriod();
		( *it )->m_bad_lfoShapeData = true;
	}
}

void EnvelopeAndLfoParameters::LfoInstances::reset()
{
	QMutexLocker m( &m_lfoListMutex );
	for( LfoList::Iterator it = m_lfos.begin();
							it != m_lfos.end(); ++it )
	{
		( *it )->m_lfoFrame = 0;
		( *it )->m_bad_lfoShapeData = true;
	}
}

void EnvelopeAndLfoParameters::LfoInstances::add( EnvelopeAndLfoParameters * lfo )
{
	QMutexLocker m( &m_lfoListMutex );
	m_lfos.append( lfo );
}

void EnvelopeAndLfoParameters::LfoInstances::remove( EnvelopeAndLfoParameters * lfo )
{
	QMutexLocker m( &m_lfoListMutex );
	m_lfos.removeAll( lfo );
}

EnvelopeAndLfoParameters::EnvelopeAndLfoParameters(
					float _value_for_zero_amount,
							Model * _parent ) :
	Model( _parent ),
	m_used( false ),
	m_predelayModel( 0.0, 0.0, 2.0, 0.001, this, tr( "Env pre-delay" ) ),
	m_attackModel( 0.0, 0.0, 2.0, 0.001, this, tr( "Env attack" ) ),
	m_holdModel( 0.5, 0.0, 2.0, 0.001, this, tr( "Env hold" ) ),
	m_decayModel( 0.5, 0.0, 2.0, 0.001, this, tr( "Env decay" ) ),
	m_sustainModel( 0.5, 0.0, 1.0, 0.001, this, tr( "Env sustain" ) ),
	m_releaseModel( 0.1, 0.0, 2.0, 0.001, this, tr( "Env release" ) ),
	m_amountModel( 0.0, -1.0, 1.0, 0.005, this, tr( "Env mod amount" ) ),
	m_valueForZeroAmount( _value_for_zero_amount ),
	m_pahdFrames( 0 ),
	m_rFrames( 0 ),
	m_pahdEnv( NULL ),
	m_rEnv( NULL ),
	m_pahdBufSize( 0 ),
	m_rBufSize( 0 ),
	m_lfoPredelayModel( 0.0, 0.0, 1.0, 0.001, this, tr( "LFO pre-delay" ) ),
	m_lfoAttackModel( 0.0, 0.0, 1.0, 0.001, this, tr( "LFO attack" ) ),
	m_lfoSpeedModel( 0.1, 0.001, 1.0, 0.0001,
				SECS_PER_LFO_OSCILLATION * 1000.0, this,
							tr( "LFO frequency" ) ),
	m_lfoAmountModel( 0.0, -1.0, 1.0, 0.005, this, tr( "LFO mod amount" ) ),
	m_lfoWaveModel( SineWave, 0, NumLfoShapes, this, tr( "LFO wave shape" ) ),
	m_x100Model( false, this, tr( "LFO frequency x 100" ) ),
	m_controlEnvAmountModel( false, this, tr( "Modulate env amount" ) ),
	m_lfoFrame( 0 ),
	m_lfoAmountIsZero( false ),
	m_lfoShapeData( NULL )
{
	m_amountModel.setCenterValue( 0 );
	m_lfoAmountModel.setCenterValue( 0 );

	if( s_lfoInstances == NULL )
	{
		s_lfoInstances = new LfoInstances();
	}

	instances()->add( this );

	connect( &m_predelayModel, SIGNAL( dataChanged() ),
			this, SLOT( updateSampleVars() ), Qt::DirectConnection );
	connect( &m_attackModel, SIGNAL( dataChanged() ),
			this, SLOT( updateSampleVars() ), Qt::DirectConnection );
	connect( &m_holdModel, SIGNAL( dataChanged() ),
			this, SLOT( updateSampleVars() ), Qt::DirectConnection );
	connect( &m_decayModel, SIGNAL( dataChanged() ),
			this, SLOT( updateSampleVars() ), Qt::DirectConnection );
	connect( &m_sustainModel, SIGNAL( dataChanged() ),
			this, SLOT( updateSampleVars() ), Qt::DirectConnection );
	connect( &m_releaseModel, SIGNAL( dataChanged() ),
			this, SLOT( updateSampleVars() ), Qt::DirectConnection );
	connect( &m_amountModel, SIGNAL( dataChanged() ),
			this, SLOT( updateSampleVars() ), Qt::DirectConnection );

	connect( &m_lfoPredelayModel, SIGNAL( dataChanged() ),
			this, SLOT( updateSampleVars() ), Qt::DirectConnection );
	connect( &m_lfoAttackModel, SIGNAL( dataChanged() ),
			this, SLOT( updateSampleVars() ), Qt::DirectConnection );
	connect( &m_lfoSpeedModel, SIGNAL( dataChanged() ),
			this, SLOT( updateSampleVars() ), Qt::DirectConnection );
	connect( &m_lfoAmountModel, SIGNAL( dataChanged() ),
			this, SLOT( updateSampleVars() ), Qt::DirectConnection );
	connect( &m_lfoWaveModel, SIGNAL( dataChanged() ),
			this, SLOT( updateSampleVars() ), Qt::DirectConnection );
	connect( &m_x100Model, SIGNAL( dataChanged() ),
			this, SLOT( updateSampleVars() ), Qt::DirectConnection );

	connect( Engine::mixer(), SIGNAL( sampleRateChanged() ),
				this, SLOT( updateSampleVars() ) );

	m_lfoShapeData =
		new sample_t[Engine::mixer()->framesPerPeriod()];

	updateSampleVars();
}

EnvelopeAndLfoParameters::~EnvelopeAndLfoParameters()
{
	m_predelayModel.disconnect( this );
	m_attackModel.disconnect( this );
	m_holdModel.disconnect( this );
	m_decayModel.disconnect( this );
	m_sustainModel.disconnect( this );
	m_releaseModel.disconnect( this );
	m_amountModel.disconnect( this );
	m_lfoPredelayModel.disconnect( this );
	m_lfoAttackModel.disconnect( this );
	m_lfoSpeedModel.disconnect( this );
	m_lfoAmountModel.disconnect( this );
	m_lfoWaveModel.disconnect( this );
	m_x100Model.disconnect( this );

	delete[] m_pahdEnv;
	delete[] m_rEnv;
	delete[] m_lfoShapeData;

	instances()->remove( this );

	if( instances()->isEmpty() )
	{
		delete instances();
		s_lfoInstances = NULL;
	}
}

inline sample_t EnvelopeAndLfoParameters::lfoShapeSample( fpp_t _frame_offset )
{
	f_cnt_t frame = ( m_lfoFrame + _frame_offset ) % m_lfoOscillationFrames;
	const float phase = frame / static_cast<float>(
						m_lfoOscillationFrames );
	sample_t shape_sample;
	switch( m_lfoWaveModel.value()  )
	{
		case TriangleWave:
			shape_sample = Oscillator::triangleSample( phase );
			break;
		case SquareWave:
			shape_sample = Oscillator::squareSample( phase );
			break;
		case SawWave:
			shape_sample = Oscillator::sawSample( phase );
			break;
		case UserDefinedWave:
			shape_sample = m_userWave.userWaveSample( phase );
			break;
		case RandomWave:
			if( frame == 0 )
			{
				m_random = Oscillator::noiseSample( 0.0f );
			}
			shape_sample = m_random;
			break;
		case SineWave:
		default:
			shape_sample = Oscillator::sinSample( phase );
			break;
	}
	return shape_sample * m_lfoAmount;
}

void EnvelopeAndLfoParameters::updateLfoShapeData()
{
	const fpp_t frames = Engine::mixer()->framesPerPeriod();
	for( fpp_t offset = 0; offset < frames; ++offset )
	{
		m_lfoShapeData[offset] = lfoShapeSample( offset );
	}
	m_bad_lfoShapeData = false;
}

inline void EnvelopeAndLfoParameters::fillLfoLevel( float * _buf,
							f_cnt_t _frame,
							const fpp_t _frames )
{
	if( m_lfoAmountIsZero || _frame <= m_lfoPredelayFrames )
	{
		for( fpp_t offset = 0; offset < _frames; ++offset )
		{
			*_buf++ = 0.0f;
		}
		return;
	}
	_frame -= m_lfoPredelayFrames;

	if( m_bad_lfoShapeData )
	{
		updateLfoShapeData();
	}

	fpp_t offset = 0;
	const float lafI = 1.0f / qMax( minimumFrames, m_lfoAttackFrames );
	for( ; offset < _frames && _frame < m_lfoAttackFrames; ++offset,
								++_frame )
	{
		*_buf++ = m_lfoShapeData[offset] * _frame * lafI;
	}
	for( ; offset < _frames; ++offset )
	{
		*_buf++ = m_lfoShapeData[offset];
	}
}

void EnvelopeAndLfoParameters::fillLevel( float * _buf, f_cnt_t _frame,
						const f_cnt_t _release_begin,
						const fpp_t _frames )
{
	QMutexLocker m(&m_paramMutex);

	if( _frame < 0 || _release_begin < 0 )
	{
		return;
	}

	fillLfoLevel( _buf, _frame, _frames );

	for( fpp_t offset = 0; offset < _frames; ++offset, ++_buf, ++_frame )
	{
		float env_level;
		if( _frame < _release_begin )
		{
			if( _frame < m_pahdFrames )
			{
				env_level = m_pahdEnv[_frame];
			}
			else
			{
				env_level = m_sustainLevel;
			}
		}
		else if( ( _frame - _release_begin ) < m_rFrames )
		{
			env_level = m_rEnv[_frame - _release_begin] *
				( ( _release_begin < m_pahdFrames ) ?
				m_pahdEnv[_release_begin] : m_sustainLevel );
		}
		else
		{
			env_level = 0.0f;
		}

		// at this point, *_buf is LFO level
		*_buf = m_controlEnvAmountModel.value() ?
			env_level * ( 0.5f + *_buf ) :
			env_level + *_buf;
	}
}

void EnvelopeAndLfoParameters::saveSettings( QDomDocument & _doc,
							QDomElement & _parent )
{
	m_predelayModel.saveSettings( _doc, _parent, "pdel" );
	m_attackModel.saveSettings( _doc, _parent, "att" );
	m_holdModel.saveSettings( _doc, _parent, "hold" );
	m_decayModel.saveSettings( _doc, _parent, "dec" );
	m_sustainModel.saveSettings( _doc, _parent, "sustain" );
	m_releaseModel.saveSettings( _doc, _parent, "rel" );
	m_amountModel.saveSettings( _doc, _parent, "amt" );
	m_lfoWaveModel.saveSettings( _doc, _parent, "lshp" );
	m_lfoPredelayModel.saveSettings( _doc, _parent, "lpdel" );
	m_lfoAttackModel.saveSettings( _doc, _parent, "latt" );
	m_lfoSpeedModel.saveSettings( _doc, _parent, "lspd" );
	m_lfoAmountModel.saveSettings( _doc, _parent, "lamt" );
	m_x100Model.saveSettings( _doc, _parent, "x100" );
	m_controlEnvAmountModel.saveSettings( _doc, _parent, "ctlenvamt" );
	_parent.setAttribute( "userwavefile", m_userWave.audioFile() );
}

void EnvelopeAndLfoParameters::loadSettings( const QDomElement & _this )
{
	m_predelayModel.loadSettings( _this, "pdel" );
	m_attackModel.loadSettings( _this, "att" );
	m_holdModel.loadSettings( _this, "hold" );
	m_decayModel.loadSettings( _this, "dec" );
	m_sustainModel.loadSettings( _this, "sustain" );
	m_releaseModel.loadSettings( _this, "rel" );
	m_amountModel.loadSettings( _this, "amt" );
	m_lfoWaveModel.loadSettings( _this, "lshp" );
	m_lfoPredelayModel.loadSettings( _this, "lpdel" );
	m_lfoAttackModel.loadSettings( _this, "latt" );
	m_lfoSpeedModel.loadSettings( _this, "lspd" );
	m_lfoAmountModel.loadSettings( _this, "lamt" );
	m_x100Model.loadSettings( _this, "x100" );
	m_controlEnvAmountModel.loadSettings( _this, "ctlenvamt" );

/*	 ### TODO:
	Old reversed sustain kept for backward compatibility
	with 4.15 file format*/
	if( _this.hasAttribute( "sus" ) )
	{
		m_sustainModel.loadSettings( _this, "sus" );
		m_sustainModel.setValue( 1.0 - m_sustainModel.value() );
	}

	m_userWave.setAudioFile( _this.attribute( "userwavefile" ) );

	updateSampleVars();
}

void EnvelopeAndLfoParameters::updateSampleVars()
{
	QMutexLocker m(&m_paramMutex);

	const float frames_per_env_seg = SECS_PER_ENV_SEGMENT *
				Engine::mixer()->processingSampleRate();

	// TODO: Remove the expKnobVals, time should be linear
	const f_cnt_t predelay_frames = static_cast<f_cnt_t>(
							frames_per_env_seg *
					expKnobVal( m_predelayModel.value() ) );

	const f_cnt_t attack_frames = qMax( minimumFrames,
					static_cast<f_cnt_t>( frames_per_env_seg *
					expKnobVal( m_attackModel.value() ) ) );

	const f_cnt_t hold_frames = static_cast<f_cnt_t>( frames_per_env_seg *
					expKnobVal( m_holdModel.value() ) );

	const f_cnt_t decay_frames = qMax( minimumFrames,
					static_cast<f_cnt_t>( frames_per_env_seg *
					expKnobVal( m_decayModel.value() *
					( 1 - m_sustainModel.value() ) ) ) );

	m_sustainLevel = m_sustainModel.value();
	m_amount = m_amountModel.value();
	if( m_amount >= 0 )
	{
		m_amountAdd = ( 1.0f - m_amount ) * m_valueForZeroAmount;
	}
	else
	{
		m_amountAdd = m_valueForZeroAmount;
	}

	m_pahdFrames = predelay_frames + attack_frames + hold_frames +
								decay_frames;
	m_rFrames = static_cast<f_cnt_t>( frames_per_env_seg *
					expKnobVal( m_releaseModel.value() ) );
	m_rFrames = qMax( minimumFrames, m_rFrames );

	if( static_cast<int>( floorf( m_amount * 1000.0f ) ) == 0 )
	{
		m_rFrames = minimumFrames;
	}

	// if the buffers are too small, make bigger ones - so we only alloc new memory when necessary
	if( m_pahdBufSize < m_pahdFrames )
	{
		sample_t * tmp = m_pahdEnv;
		m_pahdEnv = new sample_t[m_pahdFrames];
		delete[] tmp;
		m_pahdBufSize = m_pahdFrames;
	}
	if( m_rBufSize < m_rFrames )
	{
		sample_t * tmp = m_rEnv;
		m_rEnv = new sample_t[m_rFrames];
		delete[] tmp;
		m_rBufSize = m_rFrames;
	}

	const float aa = m_amountAdd;
	for( f_cnt_t i = 0; i < predelay_frames; ++i )
	{
		m_pahdEnv[i] = aa;
	}

	f_cnt_t add = predelay_frames;

	const float afI = ( 1.0f / attack_frames ) * m_amount;
	for( f_cnt_t i = 0; i < attack_frames; ++i )
	{
		m_pahdEnv[add+i] = i * afI + aa;
	}

	add += attack_frames;
	const float amsum = m_amount + m_amountAdd;
	for( f_cnt_t i = 0; i < hold_frames; ++i )
	{
		m_pahdEnv[add + i] = amsum;
	}

	add += hold_frames;
	const float dfI = ( 1.0 / decay_frames ) * ( m_sustainLevel -1 ) * m_amount;
	for( f_cnt_t i = 0; i < decay_frames; ++i )
	{
/*
		m_pahdEnv[add + i] = ( m_sustainLevel + ( 1.0f -
						(float)i / decay_frames ) *
						( 1.0f - m_sustainLevel ) ) *
							m_amount + m_amountAdd;
*/
		m_pahdEnv[add + i] = amsum + i*dfI;
	}

	const float rfI = ( 1.0f / m_rFrames ) * m_amount;
	for( f_cnt_t i = 0; i < m_rFrames; ++i )
	{
		m_rEnv[i] = (float)( m_rFrames - i ) * rfI;
	}

	// save this calculation in real-time-part
	m_sustainLevel = m_sustainLevel * m_amount + m_amountAdd;

	const float frames_per_lfo_oscillation = SECS_PER_LFO_OSCILLATION *
				Engine::mixer()->processingSampleRate();
	m_lfoPredelayFrames = static_cast<f_cnt_t>( frames_per_lfo_oscillation *
				expKnobVal( m_lfoPredelayModel.value() ) );
	m_lfoAttackFrames = static_cast<f_cnt_t>( frames_per_lfo_oscillation *
				expKnobVal( m_lfoAttackModel.value() ) );
	m_lfoOscillationFrames = static_cast<f_cnt_t>(
						frames_per_lfo_oscillation *
						m_lfoSpeedModel.value() );
	if( m_x100Model.value() )
	{
		m_lfoOscillationFrames /= 100;
	}
	m_lfoAmount = m_lfoAmountModel.value() * 0.5f;

	m_used = true;
	if( static_cast<int>( floorf( m_lfoAmount * 1000.0f ) ) == 0 )
	{
		m_lfoAmountIsZero = true;
		if( static_cast<int>( floorf( m_amount * 1000.0f ) ) == 0 )
		{
			m_used = false;
		}
	}
	else
	{
		m_lfoAmountIsZero = false;
	}

	m_bad_lfoShapeData = true;

	emit dataChanged();

}


code:
/*
 * FxMixer.cpp - effect mixer for LMMS
 *
 * Copyright (c) 2008-2011 Tobias Doerffel <tobydox/at/users.sourceforge.net>
 *
 * This file is part of LMMS - https://lmms.io
 *
 * 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.
 *
 */

#include <QDomElement>

#include "BufferManager.h"
#include "FxMixer.h"
#include "Mixer.h"
#include "MixerWorkerThread.h"
#include "MixHelpers.h"
#include "Song.h"

#include "InstrumentTrack.h"
#include "SampleTrack.h"
#include "BBTrackContainer.h"
#include "TrackContainer.h" // For TrackContainer::TrackList typedef

FxRoute::FxRoute( FxChannel * from, FxChannel * to, float amount ) :
	m_from( from ),
	m_to( to ),
	m_amount( amount, 0, 1, 0.001, NULL,
			tr( "Amount to send from channel %1 to channel %2" ).arg( m_from->m_channelIndex ).arg( m_to->m_channelIndex ) )
{
	//qDebug( "created: %d to %d", m_from->m_channelIndex, m_to->m_channelIndex );
	// create send amount model
}

FxRoute::~FxRoute()
{
}

void FxRoute::updateName()
{
	m_amount.setDisplayName(
			tr( "Amount to send from channel %1 to channel %2" ).arg( m_from->m_channelIndex ).arg( m_to->m_channelIndex ) );
}

FxChannel::FxChannel( int idx, Model * _parent ) :
	m_fxChain( NULL ),
	m_hasInput( false ),
	m_stillRunning( false ),
	m_peakLeft( 0.0f ),
	m_peakRight( 0.0f ),
	m_buffer( new sampleFrame[Engine::mixer()->framesPerPeriod()] ),
	m_muteModel( false, _parent ),
	m_soloModel( false, _parent ),
	m_volumeModel( 1.0, 0.0, 2.0, 0.001, _parent ),
	m_name(),
	m_lock(),
	m_channelIndex( idx ),
	m_queued( false ),
	m_hasColor( false ),
	m_dependenciesMet(0)
{
	BufferManager::clear( m_buffer, Engine::mixer()->framesPerPeriod() );
}

FxChannel::~FxChannel()
{
	delete[] m_buffer;
}

inline void FxChannel::processed()
{
	for( const FxRoute * receiverRoute : m_sends )
	{
		if( receiverRoute->receiver()->m_muted == false )
		{
			receiverRoute->receiver()->incrementDeps();
		}
	}
}

void FxChannel::incrementDeps()
{
	int i = m_dependenciesMet++ + 1;
	if( i >= m_receives.size() && ! m_queued )
	{
		m_queued = true;
		MixerWorkerThread::addJob( this );
	}
}

void FxChannel::unmuteForSolo()
{
	//TODO: Recursively activate every channel, this channel sends to
	m_muteModel.setValue(false);
}

void FxChannel::doProcessing()
{
	const fpp_t fpp = Engine::mixer()->framesPerPeriod();

	if( m_muted == false )
	{
		for( FxRoute * senderRoute : m_receives )
		{
			FxChannel * sender = senderRoute->sender();
			FloatModel * sendModel = senderRoute->amount();
			if( ! sendModel ) qFatal( "Error: no send model found from %d to %d", senderRoute->senderIndex(), m_channelIndex );

			if( sender->m_hasInput || sender->m_stillRunning )
			{
				// figure out if we're getting sample-exact input
				ValueBuffer * sendBuf = sendModel->valueBuffer();
				ValueBuffer * volBuf = sender->m_volumeModel.valueBuffer();

				// mix it's output with this one's output
				sampleFrame * ch_buf = sender->m_buffer;

				// use sample-exact mixing if sample-exact values are available
				if( ! volBuf && ! sendBuf ) // neither volume nor send has sample-exact data...
				{
					const float v = sender->m_volumeModel.value() * sendModel->value();
					MixHelpers::addSanitizedMultiplied( m_buffer, ch_buf, v, fpp );
				}
				else if( volBuf && sendBuf ) // both volume and send have sample-exact data
				{
					MixHelpers::addSanitizedMultipliedByBuffers( m_buffer, ch_buf, volBuf, sendBuf, fpp );
				}
				else if( volBuf ) // volume has sample-exact data but send does not
				{
					const float v = sendModel->value();
					MixHelpers::addSanitizedMultipliedByBuffer( m_buffer, ch_buf, v, volBuf, fpp );
				}
				else // vice versa
				{
					const float v = sender->m_volumeModel.value();
					MixHelpers::addSanitizedMultipliedByBuffer( m_buffer, ch_buf, v, sendBuf, fpp );
				}
				m_hasInput = true;
			}
		}

		const float v = m_volumeModel.value();

		if( m_hasInput )
		{
			// only start fxchain when we have input...
			m_fxChain.startRunning();
		}

		m_stillRunning = m_fxChain.processAudioBuffer( m_buffer, fpp, m_hasInput );

		Mixer::StereoSample peakSamples = Engine::mixer()->getPeakValues(m_buffer, fpp);
		m_peakLeft = qMax( m_peakLeft, peakSamples.left * v );
		m_peakRight = qMax( m_peakRight, peakSamples.right * v );
	}
	else
	{
		m_peakLeft = m_peakRight = 0.0f;
	}

	// increment dependency counter of all receivers
	processed();
}

FxMixer::FxMixer() :
	Model( NULL ),
	JournallingObject(),
	m_fxChannels()
{
	// create master channel
	createChannel();
	m_lastSoloed = -1;
}

FxMixer::~FxMixer()
{
	while( ! m_fxRoutes.isEmpty() )
	{
		deleteChannelSend( m_fxRoutes.first() );
	}
	while( m_fxChannels.size() )
	{
		FxChannel * f = m_fxChannels[m_fxChannels.size() - 1];
		m_fxChannels.pop_back();
		delete f;
	}
}

int FxMixer::createChannel()
{
	const int index = m_fxChannels.size();
	// create new channel
	m_fxChannels.push_back( new FxChannel( index, this ) );

	// reset channel state
	clearChannel( index );

	return index;
}

void FxMixer::activateSolo()
{
	for (int i = 1; i < m_fxChannels.size(); ++i)
	{
		m_fxChannels[i]->m_muteBeforeSolo = m_fxChannels[i]->m_muteModel.value();
		m_fxChannels[i]->m_muteModel.setValue( true );
	}
}

void FxMixer::deactivateSolo()
{
	for (int i = 1; i < m_fxChannels.size(); ++i)
	{
		m_fxChannels[i]->m_muteModel.setValue( m_fxChannels[i]->m_muteBeforeSolo );
	}
}

void FxMixer::toggledSolo()
{
	int soloedChan = -1;
	bool resetSolo = m_lastSoloed != -1;
	//untoggle if lastsoloed is entered
	if (resetSolo)
	{
		m_fxChannels[m_lastSoloed]->m_soloModel.setValue( false );
	}
	//determine the soloed channel
	for (int i = 0; i < m_fxChannels.size(); ++i)
	{
		if (m_fxChannels[i]->m_soloModel.value() == true)
			soloedChan = i;
	}
	// if no channel is soloed, unmute everything, else mute everything
	if (soloedChan != -1)
	{
		if (resetSolo)
		{
			deactivateSolo();
			activateSolo();
		} else {
			activateSolo();
		}
		// unmute the soloed chan and every channel it sends to
		m_fxChannels[soloedChan]->unmuteForSolo();
	} else {
		deactivateSolo();
	}
	m_lastSoloed = soloedChan;
}

void FxMixer::deleteChannel( int index )
{
	// channel deletion is performed between mixer rounds
	Engine::mixer()->requestChangeInModel();

	// go through every instrument and adjust for the channel index change
	TrackContainer::TrackList tracks;
	tracks += Engine::getSong()->tracks();
	tracks += Engine::getBBTrackContainer()->tracks();

	for( Track* t : tracks )
	{
		if( t->type() == Track::InstrumentTrack )
		{
			InstrumentTrack* inst = dynamic_cast<InstrumentTrack *>( t );
			int val = inst->effectChannelModel()->value(0);
			if( val == index )
			{
				// we are deleting this track's fx send
				// send to master
				inst->effectChannelModel()->setValue(0);
			}
			else if( val > index )
			{
				// subtract 1 to make up for the missing channel
				inst->effectChannelModel()->setValue(val-1);
			}
		}
		else if( t->type() == Track::SampleTrack )
		{
			SampleTrack* strk = dynamic_cast<SampleTrack *>( t );
			int val = strk->effectChannelModel()->value(0);
			if( val == index )
			{
				// we are deleting this track's fx send
				// send to master
				strk->effectChannelModel()->setValue(0);
			}
			else if( val > index )
			{
				// subtract 1 to make up for the missing channel
				strk->effectChannelModel()->setValue(val-1);
			}
		}
	}

	FxChannel * ch = m_fxChannels[index];

	// delete all of this channel's sends and receives
	while( ! ch->m_sends.isEmpty() )
	{
		deleteChannelSend( ch->m_sends.first() );
	}
	while( ! ch->m_receives.isEmpty() )
	{
		deleteChannelSend( ch->m_receives.first() );
	}

	// if m_lastSoloed was our index, reset it
	if (m_lastSoloed == index) { m_lastSoloed = -1; }
	// if m_lastSoloed is > delete index, it will move left
	else if (m_lastSoloed > index) { --m_lastSoloed; }

	// actually delete the channel
	m_fxChannels.remove(index);
	delete ch;

	for( int i = index; i < m_fxChannels.size(); ++i )
	{
		validateChannelName( i, i + 1 );

		// set correct channel index
		m_fxChannels[i]->m_channelIndex = i;

		// now check all routes and update names of the send models
		for( FxRoute * r : m_fxChannels[i]->m_sends )
		{
			r->updateName();
		}
		for( FxRoute * r : m_fxChannels[i]->m_receives )
		{
			r->updateName();
		}
	}

	Engine::mixer()->doneChangeInModel();
}

void FxMixer::moveChannelLeft( int index )
{
	// can't move master or first channel
	if( index <= 1 || index >= m_fxChannels.size() )
	{
		return;
	}
	// channels to swap
	int a = index - 1, b = index;

	// check if m_lastSoloed is one of our swaps
	if (m_lastSoloed == a) { m_lastSoloed = b; }
	else if (m_lastSoloed == b) { m_lastSoloed = a; }

	// go through every instrument and adjust for the channel index change
	TrackContainer::TrackList songTrackList = Engine::getSong()->tracks();
	TrackContainer::TrackList bbTrackList = Engine::getBBTrackContainer()->tracks();

	TrackContainer::TrackList trackLists[] = {songTrackList, bbTrackList};
	for(int tl=0; tl<2; ++tl)
	{
		TrackContainer::TrackList trackList = trackLists[tl];
		for(int i=0; i<trackList.size(); ++i)
		{
			if( trackList[i]->type() == Track::InstrumentTrack )
			{
				InstrumentTrack * inst = (InstrumentTrack *) trackList[i];
				int val = inst->effectChannelModel()->value(0);
				if( val == a )
				{
					inst->effectChannelModel()->setValue(b);
				}
				else if( val == b )
				{
					inst->effectChannelModel()->setValue(a);
				}
			}
			else if( trackList[i]->type() == Track::SampleTrack )
			{
				SampleTrack * strk = (SampleTrack *) trackList[i];
				int val = strk->effectChannelModel()->value(0);
				if( val == a )
				{
					strk->effectChannelModel()->setValue(b);
				}
				else if( val == b )
				{
					strk->effectChannelModel()->setValue(a);
				}
			}
		}
	}

	// Swap positions in array
	qSwap(m_fxChannels[index], m_fxChannels[index - 1]);

	// Update m_channelIndex of both channels
	m_fxChannels[index]->m_channelIndex = index;
	m_fxChannels[index - 1]->m_channelIndex = index -1;
}

void FxMixer::moveChannelRight( int index )
{
	moveChannelLeft( index + 1 );
}

FxRoute * FxMixer::createChannelSend( fx_ch_t fromChannel, fx_ch_t toChannel,
								float amount )
{
//	qDebug( "requested: %d to %d", fromChannel, toChannel );
	// find the existing connection
	FxChannel * from = m_fxChannels[fromChannel];
	FxChannel * to = m_fxChannels[toChannel];

	for( int i=0; i<from->m_sends.size(); ++i )
	{
		if( from->m_sends[i]->receiver() == to )
		{
			// simply adjust the amount
			from->m_sends[i]->amount()->setValue( amount );
			return from->m_sends[i];
		}
	}

	// connection does not exist. create a new one
	return createRoute( from, to, amount );
}

FxRoute * FxMixer::createRoute( FxChannel * from, FxChannel * to, float amount )
{
	if( from == to )
	{
		return NULL;
	}
	Engine::mixer()->requestChangeInModel();
	FxRoute * route = new FxRoute( from, to, amount );

	// add us to from's sends
	from->m_sends.append( route );

	// add us to to's receives
	to->m_receives.append( route );

	// add us to fxmixer's list
	Engine::fxMixer()->m_fxRoutes.append( route );
	Engine::mixer()->doneChangeInModel();

	return route;
}

// delete the connection made by createChannelSend
void FxMixer::deleteChannelSend( fx_ch_t fromChannel, fx_ch_t toChannel )
{
	// delete the send
	FxChannel * from = m_fxChannels[fromChannel];
	FxChannel * to	 = m_fxChannels[toChannel];

	// find and delete the send entry
	for( int i = 0; i < from->m_sends.size(); ++i )
	{
		if( from->m_sends[i]->receiver() == to )
		{
			deleteChannelSend( from->m_sends[i] );
			break;
		}
	}
}

void FxMixer::deleteChannelSend( FxRoute * route )
{
	Engine::mixer()->requestChangeInModel();
	// remove us from from's sends
	route->sender()->m_sends.remove( route->sender()->m_sends.indexOf( route ) );
	// remove us from to's receives
	route->receiver()->m_receives.remove( route->receiver()->m_receives.indexOf( route ) );
	// remove us from fxmixer's list
	Engine::fxMixer()->m_fxRoutes.remove( Engine::fxMixer()->m_fxRoutes.indexOf( route ) );
	delete route;
	Engine::mixer()->doneChangeInModel();
}

bool FxMixer::isInfiniteLoop( fx_ch_t sendFrom, fx_ch_t sendTo )
{
	if( sendFrom == sendTo ) return true;
	FxChannel * from = m_fxChannels[sendFrom];
	FxChannel * to = m_fxChannels[sendTo];
	bool b = checkInfiniteLoop( from, to );
	return b;
}

bool FxMixer::checkInfiniteLoop( FxChannel * from, FxChannel * to )
{
	// can't send master to anything
	if( from == m_fxChannels[0] )
	{
		return true;
	}

	// can't send channel to itself
	if( from == to )
	{
		return true;
	}

	// follow sendTo's outputs recursively looking for something that sends
	// to sendFrom
	for( int i=0; i < to->m_sends.size(); ++i )
	{
		if( checkInfiniteLoop( from, to->m_sends[i]->receiver() ) )
		{
			return true;
		}
	}

	return false;
}

// how much does fromChannel send its output to the input of toChannel?
FloatModel * FxMixer::channelSendModel( fx_ch_t fromChannel, fx_ch_t toChannel )
{
	if( fromChannel == toChannel )
	{
		return NULL;
	}
	const FxChannel * from = m_fxChannels[fromChannel];
	const FxChannel * to = m_fxChannels[toChannel];

	for( FxRoute * route : from->m_sends )
	{
		if( route->receiver() == to )
		{
			return route->amount();
		}
	}

	return NULL;
}

void FxMixer::mixToChannel( const sampleFrame * _buf, fx_ch_t _ch )
{
	if( m_fxChannels[_ch]->m_muteModel.value() == false )
	{
		m_fxChannels[_ch]->m_lock.lock();
		MixHelpers::add( m_fxChannels[_ch]->m_buffer, _buf, Engine::mixer()->framesPerPeriod() );
		m_fxChannels[_ch]->m_hasInput = true;
		m_fxChannels[_ch]->m_lock.unlock();
	}
}

void FxMixer::prepareMasterMix()
{
	BufferManager::clear( m_fxChannels[0]->m_buffer,
					Engine::mixer()->framesPerPeriod() );
}

void FxMixer::masterMix( sampleFrame * _buf )
{
	const int fpp = Engine::mixer()->framesPerPeriod();

	// add the channels that have no dependencies (no incoming senders, ie.
	// no receives) to the jobqueue. The channels that have receives get
	// added when their senders get processed, which is detected by
	// dependency counting.
	// also instantly add all muted channels as they don't need to care
	// about their senders, and can just increment the deps of their
	// recipients right away.
	MixerWorkerThread::resetJobQueue( MixerWorkerThread::JobQueue::Dynamic );
	for( FxChannel * ch : m_fxChannels )
	{
		ch->m_muted = ch->m_muteModel.value();
		if( ch->m_muted ) // instantly "process" muted channels
		{
			ch->processed();
			ch->done();
		}
		else if( ch->m_receives.size() == 0 )
		{
			ch->m_queued = true;
			MixerWorkerThread::addJob( ch );
		}
	}
	while (m_fxChannels[0]->state() != ThreadableJob::ProcessingState::Done)
	{
		bool found = false;
		for( FxChannel * ch : m_fxChannels )
		{
			const auto s = ch->state();
			if (s == ThreadableJob::ProcessingState::Queued
				|| s == ThreadableJob::ProcessingState::InProgress)
			{
				found = true;
				break;
			}
		}
		if( !found )
		{
			break;
		}
		MixerWorkerThread::startAndWaitForJobs();
	}

	// handle sample-exact data in master volume fader
	ValueBuffer * volBuf = m_fxChannels[0]->m_volumeModel.valueBuffer();

	if( volBuf )
	{
		for( int f = 0; f < fpp; f++ )
		{
			m_fxChannels[0]->m_buffer[f][0] *= volBuf->values()[f];
			m_fxChannels[0]->m_buffer[f][1] *= volBuf->values()[f];
		}
	}

	const float v = volBuf
		? 1.0f
		: m_fxChannels[0]->m_volumeModel.value();
	MixHelpers::addSanitizedMultiplied( _buf, m_fxChannels[0]->m_buffer, v, fpp );

	// clear all channel buffers and
	// reset channel process state
	for( int i = 0; i < numChannels(); ++i)
	{
		BufferManager::clear( m_fxChannels[i]->m_buffer,
				Engine::mixer()->framesPerPeriod() );
		m_fxChannels[i]->reset();
		m_fxChannels[i]->m_queued = false;
		// also reset hasInput
		m_fxChannels[i]->m_hasInput = false;
		m_fxChannels[i]->m_dependenciesMet = 0;
	}
}

void FxMixer::clear()
{
	while( m_fxChannels.size() > 1 )
	{
		deleteChannel(1);
	}

	clearChannel(0);
}

void FxMixer::clearChannel(fx_ch_t index)
{
	FxChannel * ch = m_fxChannels[index];
	ch->m_fxChain.clear();
	ch->m_volumeModel.setValue( 1.0f );
	ch->m_muteModel.setValue( false );
	ch->m_soloModel.setValue( false );
	ch->m_name = ( index == 0 ) ? tr( "Master" ) : tr( "FX %1" ).arg( index );
	ch->m_volumeModel.setDisplayName( ch->m_name + ">" + tr( "Volume" ) );
	ch->m_muteModel.setDisplayName( ch->m_name + ">" + tr( "Mute" ) );
	ch->m_soloModel.setDisplayName( ch->m_name + ">" + tr( "Solo" ) );

	// send only to master
	if( index > 0)
	{
		// delete existing sends
		while( ! ch->m_sends.isEmpty() )
		{
			deleteChannelSend( ch->m_sends.first() );
		}

		// add send to master
		createChannelSend( index, 0 );
	}

	// delete receives
	while( ! ch->m_receives.isEmpty() )
	{
		deleteChannelSend( ch->m_receives.first() );
	}
}

void FxMixer::saveSettings( QDomDocument & _doc, QDomElement & _this )
{
	// save channels
	for( int i = 0; i < m_fxChannels.size(); ++i )
	{
		FxChannel * ch = m_fxChannels[i];

		QDomElement fxch = _doc.createElement( QString( "fxchannel" ) );
		_this.appendChild( fxch );

		ch->m_fxChain.saveState( _doc, fxch );
		ch->m_volumeModel.saveSettings( _doc, fxch, "volume" );
		ch->m_muteModel.saveSettings( _doc, fxch, "muted" );
		ch->m_soloModel.saveSettings( _doc, fxch, "soloed" );
		fxch.setAttribute( "num", i );
		fxch.setAttribute( "name", ch->m_name );
		if( ch->m_hasColor ) fxch.setAttribute( "color", ch->m_color.name() );

		// add the channel sends
		for( int si = 0; si < ch->m_sends.size(); ++si )
		{
			QDomElement sendsDom = _doc.createElement( QString( "send" ) );
			fxch.appendChild( sendsDom );

			sendsDom.setAttribute( "channel", ch->m_sends[si]->receiverIndex() );
			ch->m_sends[si]->amount()->saveSettings( _doc, sendsDom, "amount" );
		}
	}
}

// make sure we have at least num channels
void FxMixer::allocateChannelsTo(int num)
{
	while( num > m_fxChannels.size() - 1 )
	{
		createChannel();

		// delete the default send to master
		deleteChannelSend( m_fxChannels.size()-1, 0 );
	}
}

void FxMixer::loadSettings( const QDomElement & _this )
{
	clear();
	QDomNode node = _this.firstChild();

	while( ! node.isNull() )
	{
		QDomElement fxch = node.toElement();

		// index of the channel we are about to load
		int num = fxch.attribute( "num" ).toInt();

		// allocate enough channels
		allocateChannelsTo( num );

		m_fxChannels[num]->m_volumeModel.loadSettings( fxch, "volume" );
		m_fxChannels[num]->m_muteModel.loadSettings( fxch, "muted" );
		m_fxChannels[num]->m_soloModel.loadSettings( fxch, "soloed" );
		m_fxChannels[num]->m_name = fxch.attribute( "name" );
		if( fxch.hasAttribute( "color" ) )
		{
			m_fxChannels[num]->m_hasColor = true;
			m_fxChannels[num]->m_color.setNamedColor( fxch.attribute( "color" ) );
		}

		m_fxChannels[num]->m_fxChain.restoreState( fxch.firstChildElement(
			m_fxChannels[num]->m_fxChain.nodeName() ) );

		// mixer sends
		QDomNodeList chData = fxch.childNodes();
		for( unsigned int i=0; i<chData.length(); ++i )
		{
			QDomElement chDataItem = chData.at(i).toElement();
			if( chDataItem.nodeName() == QString( "send" ) )
			{
				int sendTo = chDataItem.attribute( "channel" ).toInt();
				allocateChannelsTo( sendTo ) ;
				FxRoute * fxr = createChannelSend( num, sendTo, 1.0f );
				if( fxr ) fxr->amount()->loadSettings( chDataItem, "amount" );
			}
		}

		node = node.nextSibling();
	}

	emit dataChanged();
}

void FxMixer::validateChannelName( int index, int oldIndex )
{
	if( m_fxChannels[index]->m_name == tr( "FX %1" ).arg( oldIndex ) )
	{
		m_fxChannels[index]->m_name = tr( "FX %1" ).arg( index );
	}
}


code:
/*
 * ImportFilter.cpp - base-class for all import-filters
 *
 * Copyright (c) 2006-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
 *
 * This file is part of LMMS - https://lmms.io
 *
 * 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.
 *
 */

#include <memory>
#include <QMessageBox>

#include "ImportFilter.h"
#include "Engine.h"
#include "TrackContainer.h"
#include "PluginFactory.h"
#include "ProjectJournal.h"

using std::unique_ptr;

ImportFilter::ImportFilter( const QString & _file_name,
							const Descriptor * _descriptor ) :
	Plugin( _descriptor, NULL ),
	m_file( _file_name )
{
}

ImportFilter::~ImportFilter()
{
}

void ImportFilter::import( const QString & _file_to_import,
							TrackContainer* tc )
{
	bool successful = false;

	QByteArray s = _file_to_import.toUtf8();
	s.detach();

	// do not record changes while importing files
	const bool j = Engine::projectJournal()->isJournalling();
	Engine::projectJournal()->setJournalling( false );

	for (const Plugin::Descriptor* desc : pluginFactory->descriptors(Plugin::ImportFilter))
	{
		unique_ptr<Plugin> p(Plugin::instantiate( desc->name, NULL, s.data() ));
		if( dynamic_cast<ImportFilter *>( p.get() ) != NULL &&
			dynamic_cast<ImportFilter *>( p.get() )->tryImport( tc ) )
		{
			successful = true;
			break;
		}
	}

	Engine::projectJournal()->setJournalling( j );

	if( successful == false )
	{
		QMessageBox::information( NULL,
			TrackContainer::tr( "Couldn't import file" ),
			TrackContainer::tr( "Couldn't find a filter for "
						"importing file %1.\n"
						"You should convert this file "
						"into a format supported by "
						"LMMS using another software."
						).arg( _file_to_import ),
					QMessageBox::Ok,
					QMessageBox::NoButton );
	}
}

bool ImportFilter::openFile()
{
	if( m_file.open( QFile::ReadOnly ) == false )
	{
		QMessageBox::critical( NULL,
			TrackContainer::tr( "Couldn't open file" ),
			TrackContainer::tr( "Couldn't open file %1 "
						"for reading.\nPlease make "
						"sure you have read-"
						"permission to the file and "
						"the directory containing the "
						"file and try again!" ).arg(
							m_file.fileName() ),
					QMessageBox::Ok,
					QMessageBox::NoButton );
		return false;
	}
	return true;
}


code:
/*
 * InlineAutomation.cpp - class for automating something "inline"
 *
 * Copyright (c) 2008-2010 Tobias Doerffel <tobydox/at/users.sourceforge.net>
 *
 * This file is part of LMMS - https://lmms.io
 *
 * 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.
 *
 */

#include <QDomElement>

#include "InlineAutomation.h"

void InlineAutomation::saveSettings( QDomDocument & _doc,
							QDomElement & _parent )
{
	if( hasAutomation() )
	{
		QDomElement ap = _doc.createElement(
					AutomationPattern::classNodeName() );
		QDomElement v = _doc.createElement( nodeName() );
		automationPattern()->saveSettings( _doc, v );
		ap.appendChild( v );
		_parent.appendChild( ap );
	}
}

void InlineAutomation::loadSettings( const QDomElement & _this )
{
	QDomNode node = _this.namedItem( AutomationPattern::classNodeName() );
	if( node.isElement() )
	{
		node = node.namedItem( nodeName() );
		if( node.isElement() )
		{
			automationPattern()->loadSettings(
							node.toElement() );
		}
	}
}


code:
/*
 * Instrument.cpp - base-class for all instrument-plugins (synths, samplers etc)
 *
 * Copyright (c) 2005-2009 Tobias Doerffel <tobydox/at/users.sourceforge.net>
 *
 * This file is part of LMMS - https://lmms.io
 *
 * 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.
 *
 */

#include "Instrument.h"

#include <cmath>

#include "DummyInstrument.h"
#include "InstrumentTrack.h"
#include "lmms_constants.h"

Instrument::Instrument(InstrumentTrack * _instrument_track,
			const Descriptor * _descriptor,
			const Descriptor::SubPluginFeatures::Key *key) :
	Plugin(_descriptor, NULL/* _instrument_track*/, key),
	m_instrumentTrack( _instrument_track )
{
}

void Instrument::play( sampleFrame * )
{
}

void Instrument::deleteNotePluginData( NotePlayHandle * )
{
}

f_cnt_t Instrument::beatLen( NotePlayHandle * ) const
{
	return( 0 );
}

Instrument *Instrument::instantiate(const QString &_plugin_name,
	InstrumentTrack *_instrument_track, const Descriptor::SubPluginFeatures::Key *key, bool keyFromDnd)
{
	if(keyFromDnd)
		Q_ASSERT(!key);
	// copy from above // TODO! common cleaner func
	Plugin * p = Plugin::instantiateWithKey(_plugin_name, _instrument_track, key, keyFromDnd);
	if(dynamic_cast<Instrument *>(p))
		return dynamic_cast<Instrument *>(p);
	delete p;
	return( new DummyInstrument( _instrument_track ) );
}

bool Instrument::isFromTrack( const Track * _track ) const
{
	return( m_instrumentTrack == _track );
}

// helper function for Instrument::applyFadeIn
static int countZeroCrossings(sampleFrame *buf, fpp_t start, fpp_t frames)
{
	// zero point crossing counts of all channels
	int zeroCrossings[DEFAULT_CHANNELS] = {0};
	// maximum zero point crossing of all channels
	int maxZeroCrossings = 0;

	// determine the zero point crossing counts
	for (fpp_t f = start; f < frames; ++f)
	{
		for (ch_cnt_t ch = 0; ch < DEFAULT_CHANNELS; ++ch)
		{
			// we don't want to count [-1, 0, 1] as two crossings
			if ((buf[f - 1][ch] <= 0.0 && buf[f][ch] > 0.0) ||
					(buf[f - 1][ch] >= 0.0 && buf[f][ch] < 0.0))
			{
				++zeroCrossings[ch];
				if (zeroCrossings[ch] > maxZeroCrossings)
				{
					maxZeroCrossings = zeroCrossings[ch];
				}
			}
		}
	}

	return maxZeroCrossings;
}

// helper function for Instrument::applyFadeIn
fpp_t getFadeInLength(float maxLength, fpp_t frames, int zeroCrossings)
{
	// calculate the length of the fade in
	// Length is inversely proportional to the max of zeroCrossings,
	// because for low frequencies, we need a longer fade in to
	// prevent clicking.
	return (fpp_t) (maxLength  / ((float) zeroCrossings / ((float) frames / 128.0f) + 1.0f));
}

void Instrument::applyFadeIn(sampleFrame * buf, NotePlayHandle * n)
{
	const static float MAX_FADE_IN_LENGTH = 85.0;
	f_cnt_t total = n->totalFramesPlayed();
	if (total == 0)
	{
		const fpp_t frames = n->framesLeftForCurrentPeriod();
		const f_cnt_t offset = n->offset();

		// We need to skip the first sample because it almost always
		// produces a zero crossing; it's not helpful while
		// determining the fade in length. Hence 1
		int maxZeroCrossings = countZeroCrossings(buf, offset + 1, offset + frames);

		fpp_t length = getFadeInLength(MAX_FADE_IN_LENGTH, frames, maxZeroCrossings);
		n->m_fadeInLength = length;

		// apply fade in
		length = length < frames ? length : frames;
		for (fpp_t f = 0; f < length; ++f)
		{
			for (ch_cnt_t ch = 0; ch < DEFAULT_CHANNELS; ++ch)
			{
				buf[offset + f][ch] *= 0.5 - 0.5 * cosf(F_PI * (float) f / (float) n->m_fadeInLength);
			}
		}
	}
	else if (total < n->m_fadeInLength)
	{
		const fpp_t frames = n->framesLeftForCurrentPeriod();

		int new_zc = countZeroCrossings(buf, 1, frames);
		fpp_t new_length = getFadeInLength(MAX_FADE_IN_LENGTH, frames, new_zc);

		for (fpp_t f = 0; f < frames; ++f)
		{
			for (ch_cnt_t ch = 0; ch < DEFAULT_CHANNELS; ++ch)
			{
				float currentLength = n->m_fadeInLength * (1.0f - (float) f / frames) + new_length * ((float) f / frames);
				buf[f][ch] *= 0.5 - 0.5 * cosf(F_PI * (float) (total + f) / currentLength);
				if (total + f >= currentLength)
				{
					n->m_fadeInLength = currentLength;
					return;
				}
			}
		}
		n->m_fadeInLength = new_length;
	}
}

void Instrument::applyRelease( sampleFrame * buf, const NotePlayHandle * _n )
{
	const fpp_t frames = _n->framesLeftForCurrentPeriod();
	const fpp_t fpp = Engine::mixer()->framesPerPeriod();
	const f_cnt_t fl = _n->framesLeft();
	if( fl <= desiredReleaseFrames()+fpp )
	{
		for( fpp_t f = (fpp_t)( ( fl > desiredReleaseFrames() ) ?
				( qMax( fpp - desiredReleaseFrames(), 0 ) +
					fl % fpp ) : 0 ); f < frames; ++f )
		{
			const float fac = (float)( fl-f-1 ) /
							desiredReleaseFrames();
			for( ch_cnt_t ch = 0; ch < DEFAULT_CHANNELS; ++ch )
			{
				buf[f][ch] *= fac;
			}
		}
	}
}

QString Instrument::fullDisplayName() const
{
	return instrumentTrack()->displayName();
}


code:
/*
 * InstrumentFunctions.cpp - models for instrument-function-tab
 *
 * Copyright (c) 2004-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
 *
 * This file is part of LMMS - https://lmms.io
 *
 * 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.
 *
 */

#include <QDomElement>

#include "InstrumentFunctions.h"
#include "embed.h"
#include "Engine.h"
#include "InstrumentTrack.h"
#include "Mixer.h"
#include "PresetPreviewPlayHandle.h"

InstrumentFunctionNoteStacking::ChordTable::Init InstrumentFunctionNoteStacking::ChordTable::s_initTable[] =
{
	{ QT_TRANSLATE_NOOP( "InstrumentFunctionNoteStacking", "octave" ), { 0, -1 } },
	{ QT_TRANSLATE_NOOP( "InstrumentFunctionNoteStacking", "Major" ), { 0, 4, 7, -1 } },
	{ QT_TRANSLATE_NOOP( "InstrumentFunctionNoteStacking", "Majb5" ), { 0, 4, 6, -1 } },
	{ QT_TRANSLATE_NOOP( "InstrumentFunctionNoteStacking", "minor" ), { 0, 3, 7, -1 } },
	{ QT_TRANSLATE_NOOP( "InstrumentFunctionNoteStacking", "minb5" ), { 0, 3, 6, -1 } },
	{ QT_TRANSLATE_NOOP( "InstrumentFunctionNoteStacking", "sus2" ), { 0, 2, 7, -1 } },
	{ QT_TRANSLATE_NOOP( "InstrumentFunctionNoteStacking", "sus4" ), { 0, 5, 7, -1 } },
	{ QT_TRANSLATE_NOOP( "InstrumentFunctionNoteStacking", "aug" ), { 0, 4, 8, -1 } },
	{ QT_TRANSLATE_NOOP( "InstrumentFunctionNoteStacking", "augsus4" ), { 0, 5, 8, -1 } },
	{ QT_TRANSLATE_NOOP( "InstrumentFunctionNoteStacking", "tri" ), { 0, 3, 6, 9, -1 } },

	{ QT_TRANSLATE_NOOP( "InstrumentFunctionNoteStacking", "6" ), { 0, 4, 7, 9, -1 } },
	{ QT_TRANSLATE_NOOP( "InstrumentFunctionNoteStacking", "6sus4" ), { 0, 5, 7, 9, -1 } },
	{ QT_TRANSLATE_NOOP( "InstrumentFunctionNoteStacking", "6add9" ), { 0, 4, 7, 9, 14, -1 } },
	{ QT_TRANSLATE_NOOP( "InstrumentFunctionNoteStacking", "m6" ), { 0, 3, 7, 9, -1 } },
	{ QT_TRANSLATE_NOOP( "InstrumentFunctionNoteStacking", "m6add9" ), { 0, 3, 7, 9, 14, -1 } },

	{ QT_TRANSLATE_NOOP( "InstrumentFunctionNoteStacking", "7" ), { 0, 4, 7, 10, -1 } },
	{ QT_TRANSLATE_NOOP( "InstrumentFunctionNoteStacking", "7sus4" ), { 0, 5, 7, 10, -1 } },
	{ QT_TRANSLATE_NOOP( "InstrumentFunctionNoteStacking", "7#5" ), { 0, 4, 8, 10, -1 } },
	{ QT_TRANSLATE_NOOP( "InstrumentFunctionNoteStacking", "7b5" ), { 0, 4, 6, 10, -1 } },
	{ QT_TRANSLATE_NOOP( "InstrumentFunctionNoteStacking", "7#9" ), { 0, 4, 7, 10, 15, -1 } },
	{ QT_TRANSLATE_NOOP( "InstrumentFunctionNoteStacking", "7b9" ), { 0, 4, 7, 10, 13, -1 } },
	{ QT_TRANSLATE_NOOP( "InstrumentFunctionNoteStacking", "7#5#9" ), { 0, 4, 8, 10, 15, -1 } },
	{ QT_TRANSLATE_NOOP( "InstrumentFunctionNoteStacking", "7#5b9" ), { 0, 4, 8, 10, 13, -1 } },
	{ QT_TRANSLATE_NOOP( "InstrumentFunctionNoteStacking", "7b5b9" ), { 0, 4, 6, 10, 13, -1 } },
	{ QT_TRANSLATE_NOOP( "InstrumentFunctionNoteStacking", "7add11" ), { 0, 4, 7, 10, 17, -1 } },
	{ QT_TRANSLATE_NOOP( "InstrumentFunctionNoteStacking", "7add13" ), { 0, 4, 7, 10, 21, -1 } },
	{ QT_TRANSLATE_NOOP( "InstrumentFunctionNoteStacking", "7#11" ), { 0, 4, 7, 10, 18, -1 } },
	{ QT_TRANSLATE_NOOP( "InstrumentFunctionNoteStacking", "Maj7" ), { 0, 4, 7, 11, -1 } },
	{ QT_TRANSLATE_NOOP( "InstrumentFunctionNoteStacking", "Maj7b5" ), { 0, 4, 6, 11, -1 } },
	{ QT_TRANSLATE_NOOP( "InstrumentFunctionNoteStacking", "Maj7#5" ), { 0, 4, 8, 11, -1 } },
	{ QT_TRANSLATE_NOOP( "InstrumentFunctionNoteStacking", "Maj7#11" ), { 0, 4, 7, 11, 18, -1 } },
	{ QT_TRANSLATE_NOOP( "InstrumentFunctionNoteStacking", "Maj7add13" ), { 0, 4, 7, 11, 21, -1 } },
	{ QT_TRANSLATE_NOOP( "InstrumentFunctionNoteStacking", "m7" ), { 0, 3, 7, 10, -1 } },
	{ QT_TRANSLATE_NOOP( "InstrumentFunctionNoteStacking", "m7b5" ), { 0, 3, 6, 10, -1 } },
	{ QT_TRANSLATE_NOOP( "InstrumentFunctionNoteStacking", "m7b9" ), { 0, 3, 7, 10, 13, -1 } },
	{ QT_TRANSLATE_NOOP( "InstrumentFunctionNoteStacking", "m7add11" ), { 0, 3, 7, 10, 17, -1 } },
	{ QT_TRANSLATE_NOOP( "InstrumentFunctionNoteStacking", "m7add13" ), { 0, 3, 7, 10, 21, -1 } },
	{ QT_TRANSLATE_NOOP( "InstrumentFunctionNoteStacking", "m-Maj7" ), { 0, 3, 7, 11, -1 } },
	{ QT_TRANSLATE_NOOP( "InstrumentFunctionNoteStacking", "m-Maj7add11" ), { 0, 3, 7, 11, 17, -1 } },
	{ QT_TRANSLATE_NOOP( "InstrumentFunctionNoteStacking", "m-Maj7add13" ), { 0, 3, 7, 11, 21, -1 } },

	{ QT_TRANSLATE_NOOP( "InstrumentFunctionNoteStacking", "9" ), { 0, 4, 7, 10, 14, -1 } },
	{ QT_TRANSLATE_NOOP( "InstrumentFunctionNoteStacking", "9sus4" ), { 0, 5, 7, 10, 14, -1 } },
	{ QT_TRANSLATE_NOOP( "InstrumentFunctionNoteStacking", "add9" ), { 0, 4, 7, 14, -1 } },
	{ QT_TRANSLATE_NOOP( "InstrumentFunctionNoteStacking", "9#5" ), { 0, 4, 8, 10, 14, -1 } },
	{ QT_TRANSLATE_NOOP( "InstrumentFunctionNoteStacking", "9b5" ), { 0, 4, 6, 10, 14, -1 } },
	{ QT_TRANSLATE_NOOP( "InstrumentFunctionNoteStacking", "9#11" ), { 0, 4, 7, 10, 14, 18, -1 } },
	{ QT_TRANSLATE_NOOP( "InstrumentFunctionNoteStacking", "9b13" ), { 0, 4, 7, 10, 14, 20, -1 } },
	{ QT_TRANSLATE_NOOP( "InstrumentFunctionNoteStacking", "Maj9" ), { 0, 4, 7, 11, 14, -1 } },
	{ QT_TRANSLATE_NOOP( "InstrumentFunctionNoteStacking", "Maj9sus4" ), { 0, 5, 7, 11, 15, -1 } },
	{ QT_TRANSLATE_NOOP( "InstrumentFunctionNoteStacking", "Maj9#5" ), { 0, 4, 8, 11, 14, -1 } },
	{ QT_TRANSLATE_NOOP( "InstrumentFunctionNoteStacking", "Maj9#11" ), { 0, 4, 7, 11, 14, 18, -1 } },
	{ QT_TRANSLATE_NOOP( "InstrumentFunctionNoteStacking", "m9" ), { 0, 3, 7, 10, 14, -1 } },
	{ QT_TRANSLATE_NOOP( "InstrumentFunctionNoteStacking", "madd9" ), { 0, 3, 7, 14, -1 } },
	{ QT_TRANSLATE_NOOP( "InstrumentFunctionNoteStacking", "m9b5" ), { 0, 3, 6, 10, 14, -1 } },
	{ QT_TRANSLATE_NOOP( "InstrumentFunctionNoteStacking", "m9-Maj7" ), { 0, 3, 7, 11, 14, -1 } },

	{ QT_TRANSLATE_NOOP( "InstrumentFunctionNoteStacking", "11" ), { 0, 4, 7, 10, 14, 17, -1 } },
	{ QT_TRANSLATE_NOOP( "InstrumentFunctionNoteStacking", "11b9" ), { 0, 4, 7, 10, 13, 17, -1 } },
	{ QT_TRANSLATE_NOOP( "InstrumentFunctionNoteStacking", "Maj11" ), { 0, 4, 7, 11, 14, 17, -1 } },
	{ QT_TRANSLATE_NOOP( "InstrumentFunctionNoteStacking", "m11" ), { 0, 3, 7, 10, 14, 17, -1 } },
	{ QT_TRANSLATE_NOOP( "InstrumentFunctionNoteStacking", "m-Maj11" ), { 0, 3, 7, 11, 14, 17, -1 } },

	{ QT_TRANSLATE_NOOP( "InstrumentFunctionNoteStacking", "13" ), { 0, 4, 7, 10, 14, 21, -1 } },
	{ QT_TRANSLATE_NOOP( "InstrumentFunctionNoteStacking", "13#9" ), { 0, 4, 7, 10, 15, 21, -1 } },
	{ QT_TRANSLATE_NOOP( "InstrumentFunctionNoteStacking", "13b9" ), { 0, 4, 7, 10, 13, 21, -1 } },
	{ QT_TRANSLATE_NOOP( "InstrumentFunctionNoteStacking", "13b5b9" ), { 0, 4, 6, 10, 13, 21, -1 } },
	{ QT_TRANSLATE_NOOP( "InstrumentFunctionNoteStacking", "Maj13" ), { 0, 4, 7, 11, 14, 21, -1 } },
	{ QT_TRANSLATE_NOOP( "InstrumentFunctionNoteStacking", "m13" ), { 0, 3, 7, 10, 14, 21, -1 } },
	{ QT_TRANSLATE_NOOP( "InstrumentFunctionNoteStacking", "m-Maj13" ), { 0, 3, 7, 11, 14, 21, -1 } },

	{ QT_TRANSLATE_NOOP( "InstrumentFunctionNoteStacking", "Major" ), { 0, 2, 4, 5, 7, 9, 11, -1 } },
	{ QT_TRANSLATE_NOOP( "InstrumentFunctionNoteStacking", "Harmonic minor" ), { 0, 2, 3, 5, 7, 8, 11, -1 } },
	{ QT_TRANSLATE_NOOP( "InstrumentFunctionNoteStacking", "Melodic minor" ), { 0, 2, 3, 5, 7, 9, 11, -1 } },
	{ QT_TRANSLATE_NOOP( "InstrumentFunctionNoteStacking", "Whole tone" ), { 0, 2, 4, 6, 8, 10, -1 } },
	{ QT_TRANSLATE_NOOP( "InstrumentFunctionNoteStacking", "Diminished" ), { 0, 2, 3, 5, 6, 8, 9, 11, -1 } },
	{ QT_TRANSLATE_NOOP( "InstrumentFunctionNoteStacking", "Major pentatonic" ), { 0, 2, 4, 7, 9, -1 } },
	{ QT_TRANSLATE_NOOP( "InstrumentFunctionNoteStacking", "Minor pentatonic" ), { 0, 3, 5, 7, 10, -1 } },
	{ QT_TRANSLATE_NOOP( "InstrumentFunctionNoteStacking", "Jap in sen" ), { 0, 1, 5, 7, 10, -1 } },
	{ QT_TRANSLATE_NOOP( "InstrumentFunctionNoteStacking", "Major bebop" ), { 0, 2, 4, 5, 7, 8, 9, 11, -1 } },
	{ QT_TRANSLATE_NOOP( "InstrumentFunctionNoteStacking", "Dominant bebop" ), { 0, 2, 4, 5, 7, 9, 10, 11, -1 } },
	{ QT_TRANSLATE_NOOP( "InstrumentFunctionNoteStacking", "Blues" ), { 0, 3, 5, 6, 7, 10, -1 } },
	{ QT_TRANSLATE_NOOP( "InstrumentFunctionNoteStacking", "Arabic" ), { 0, 1, 4, 5, 7, 8, 11, -1 } },
	{ QT_TRANSLATE_NOOP( "InstrumentFunctionNoteStacking", "Enigmatic" ), { 0, 1, 4, 6, 8, 10, 11, -1 } },
	{ QT_TRANSLATE_NOOP( "InstrumentFunctionNoteStacking", "Neopolitan" ), { 0, 1, 3, 5, 7, 9, 11, -1 } },
	{ QT_TRANSLATE_NOOP( "InstrumentFunctionNoteStacking", "Neopolitan minor" ), { 0, 1, 3, 5, 7, 8, 11, -1 } },
	{ QT_TRANSLATE_NOOP( "InstrumentFunctionNoteStacking", "Hungarian minor" ), { 0, 2, 3, 6, 7, 8, 11, -1 } },
	{ QT_TRANSLATE_NOOP( "InstrumentFunctionNoteStacking", "Dorian" ), { 0, 2, 3, 5, 7, 9, 10, -1 } },
	{ QT_TRANSLATE_NOOP( "InstrumentFunctionNoteStacking", "Phrygian" ), { 0, 1, 3, 5, 7, 8, 10, -1 } },
	{ QT_TRANSLATE_NOOP( "InstrumentFunctionNoteStacking", "Lydian" ), { 0, 2, 4, 6, 7, 9, 11, -1 } },
	{ QT_TRANSLATE_NOOP( "InstrumentFunctionNoteStacking", "Mixolydian" ), { 0, 2, 4, 5, 7, 9, 10, -1 } },
	{ QT_TRANSLATE_NOOP( "InstrumentFunctionNoteStacking", "Aeolian" ), { 0, 2, 3, 5, 7, 8, 10, -1 } },
	{ QT_TRANSLATE_NOOP( "InstrumentFunctionNoteStacking", "Locrian" ), { 0, 1, 3, 5, 6, 8, 10, -1 } },
	{ QT_TRANSLATE_NOOP( "InstrumentFunctionNoteStacking", "Minor" ), { 0, 2, 3, 5, 7, 8, 10, -1 } },
	{ QT_TRANSLATE_NOOP( "InstrumentFunctionNoteStacking", "Chromatic" ), { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, -1 } },
	{ QT_TRANSLATE_NOOP( "InstrumentFunctionNoteStacking", "Half-Whole Diminished" ), { 0, 1, 3, 4, 6, 7, 9, 10, -1 } },

	{ QT_TRANSLATE_NOOP( "InstrumentFunctionNoteStacking", "5" ), { 0, 7, -1 } },
	{ QT_TRANSLATE_NOOP( "InstrumentFunctionNoteStacking", "Phrygian dominant" ), { 0, 1, 4, 5, 7, 8, 10, -1 } },
	{ QT_TRANSLATE_NOOP( "InstrumentFunctionNoteStacking", "Persian" ), { 0, 1, 4, 5, 6, 8, 11, -1 } }
} ;

InstrumentFunctionNoteStacking::Chord::Chord( const char * n, const ChordSemiTones & semi_tones ) :
	m_name( InstrumentFunctionNoteStacking::tr( n ) )
{
	for( m_size = 0; m_size < MAX_CHORD_POLYPHONY; m_size++ )
	{
		if( semi_tones[m_size] == -1 )
		{
			break;
		}

		m_semiTones[m_size] = semi_tones[m_size];
	}
}

bool InstrumentFunctionNoteStacking::Chord::hasSemiTone( int8_t semi_tone ) const
{
	for( int i = 0; i < size(); ++i )
	{
		if( semi_tone == m_semiTones[i] )
		{
			return true;
		}
	}
	return false;
}

InstrumentFunctionNoteStacking::ChordTable::ChordTable() :
	QVector<Chord>()
{
	for( int i = 0;
		i < static_cast<int>( sizeof s_initTable / sizeof *s_initTable );
		i++ )
	{
		push_back( Chord( s_initTable[i].m_name, s_initTable[i].m_semiTones ) );
	}
}

const InstrumentFunctionNoteStacking::Chord & InstrumentFunctionNoteStacking::ChordTable::getByName( const QString & name, bool is_scale ) const
{
	for( int i = 0; i < size(); i++ )
	{
		if( at( i ).getName() == name && is_scale == at( i ).isScale() )
			return at( i );
	}

	static Chord empty;
	return empty;
}

InstrumentFunctionNoteStacking::InstrumentFunctionNoteStacking( Model * _parent ) :
	Model( _parent, tr( "Chords" ) ),
	m_chordsEnabledModel( false, this ),
	m_chordsModel( this, tr( "Chord type" ) ),
	m_chordRangeModel( 1.0f, 1.0f, 9.0f, 1.0f, this, tr( "Chord range" ) )
{
	const ChordTable & chord_table = ChordTable::getInstance();
	for( int i = 0; i < chord_table.size(); ++i )
	{
		m_chordsModel.addItem( chord_table[i].getName() );
	}
}

InstrumentFunctionNoteStacking::~InstrumentFunctionNoteStacking()
{
}

void InstrumentFunctionNoteStacking::processNote( NotePlayHandle * _n )
{
	const int base_note_key = _n->key();
	const ChordTable & chord_table = ChordTable::getInstance();
	// we add chord-subnotes to note if either note is a base-note and
	// arpeggio is not used or note is part of an arpeggio
	// 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->origin() == NotePlayHandle::OriginArpeggio || ( _n->hasParent() == false && _n->instrumentTrack()->isArpeggioEnabled() == false ) ) &&
			_n->totalFramesPlayed() == 0 &&
			m_chordsEnabledModel.value() == true && ! _n->isReleased() )
	{
		// then insert sub-notes for chord
		const int selected_chord = m_chordsModel.value();

		for( int octave_cnt = 0; octave_cnt < m_chordRangeModel.value(); ++octave_cnt )
		{
			const int sub_note_key_base = base_note_key + octave_cnt * KeysPerOctave;

			// process all notes in the chord
			for( int i = 0; i < chord_table[selected_chord].size(); ++i )
			{
				// add interval to sub-note-key
				const int sub_note_key = sub_note_key_base + (int) chord_table[selected_chord][i];
				// maybe we're out of range -> let's get outta
				// here!
				if( sub_note_key > NumKeys )
				{
					break;
				}
				// create copy of base-note
				Note note_copy( _n->length(), 0, sub_note_key, _n->getVolume(), _n->getPanning(), _n->detuning() );

				// create sub-note-play-handle, only note is
				// different
				Engine::mixer()->addPlayHandle(
						NotePlayHandleManager::acquire( _n->instrumentTrack(), _n->offset(), _n->frames(), note_copy,
									_n, -1, NotePlayHandle::OriginNoteStacking )
						);
			}
		}
	}
}

void InstrumentFunctionNoteStacking::saveSettings( QDomDocument & _doc, QDomElement & _this )
{
	m_chordsEnabledModel.saveSettings( _doc, _this, "chord-enabled" );
	m_chordsModel.saveSettings( _doc, _this, "chord" );
	m_chordRangeModel.saveSettings( _doc, _this, "chordrange" );
}

void InstrumentFunctionNoteStacking::loadSettings( const QDomElement & _this )
{
	m_chordsEnabledModel.loadSettings( _this, "chord-enabled" );
	m_chordsModel.loadSettings( _this, "chord" );
	m_chordRangeModel.loadSettings( _this, "chordrange" );
}

InstrumentFunctionArpeggio::InstrumentFunctionArpeggio( Model * _parent ) :
	Model( _parent, tr( "Arpeggio" ) ),
	m_arpEnabledModel( false ),
	m_arpModel( this, tr( "Arpeggio type" ) ),
	m_arpRangeModel( 1.0f, 1.0f, 9.0f, 1.0f, this, tr( "Arpeggio range" ) ),
	m_arpRepeatsModel( 1.0f, 1.0f, 8.0f, 1.0f, this, tr( "Note repeats" ) ),
	m_arpCycleModel( 0.0f, 0.0f, 6.0f, 1.0f, this, tr( "Cycle steps" ) ),
	m_arpSkipModel( 0.0f, 0.0f, 100.0f, 1.0f, this, tr( "Skip rate" ) ),
	m_arpMissModel( 0.0f, 0.0f, 100.0f, 1.0f, this, tr( "Miss rate" ) ),
	m_arpTimeModel( 200.0f, 25.0f, 2000.0f, 1.0f, 2000, this, tr( "Arpeggio time" ) ),
	m_arpGateModel( 100.0f, 1.0f, 200.0f, 1.0f, this, tr( "Arpeggio gate" ) ),
	m_arpDirectionModel( this, tr( "Arpeggio direction" ) ),
	m_arpModeModel( this, tr( "Arpeggio mode" ) )
{
	const InstrumentFunctionNoteStacking::ChordTable & chord_table = InstrumentFunctionNoteStacking::ChordTable::getInstance();
	for( int i = 0; i < chord_table.size(); ++i )
	{
		m_arpModel.addItem( chord_table[i].getName() );
	}

	m_arpDirectionModel.addItem( tr( "Up" ), std::make_unique<PixmapLoader>( "arp_up" ) );
	m_arpDirectionModel.addItem( tr( "Down" ), std::make_unique<PixmapLoader>( "arp_down" ) );
	m_arpDirectionModel.addItem( tr( "Up and down" ), std::make_unique<PixmapLoader>( "arp_up_and_down" ) );
	m_arpDirectionModel.addItem( tr( "Down and up" ), std::make_unique<PixmapLoader>( "arp_up_and_down" ) );
	m_arpDirectionModel.addItem( tr( "Random" ), std::make_unique<PixmapLoader>( "arp_random" ) );
	m_arpDirectionModel.setInitValue( ArpDirUp );

	m_arpModeModel.addItem( tr( "Free" ), std::make_unique<PixmapLoader>( "arp_free" ) );
	m_arpModeModel.addItem( tr( "Sort" ), std::make_unique<PixmapLoader>( "arp_sort" ) );
	m_arpModeModel.addItem( tr( "Sync" ), std::make_unique<PixmapLoader>( "arp_sync" ) );
}

InstrumentFunctionArpeggio::~InstrumentFunctionArpeggio()
{
}

void InstrumentFunctionArpeggio::processNote( NotePlayHandle * _n )
{
	const int base_note_key = _n->key();
	if( _n->origin() == NotePlayHandle::OriginArpeggio ||
		_n->origin() == NotePlayHandle::OriginNoteStacking ||
		!m_arpEnabledModel.value() ||
		_n->isReleased() )
	{
		return;
	}

	// Set master note if not playing arp note or it will play as an ordinary note
	_n->setMasterNote();

	const int selected_arp = m_arpModel.value();

	ConstNotePlayHandleList cnphv = NotePlayHandle::nphsOfInstrumentTrack( _n->instrumentTrack() );

	if( m_arpModeModel.value() != FreeMode && cnphv.size() == 0 )
	{
		// maybe we're playing only a preset-preview-note?
		cnphv = PresetPreviewPlayHandle::nphsOfInstrumentTrack( _n->instrumentTrack() );
		if( cnphv.size() == 0 )
		{
			// still nothing found here, so lets return
			//return;
			cnphv.push_back( _n );
		}
	}

	const InstrumentFunctionNoteStacking::ChordTable & chord_table = InstrumentFunctionNoteStacking::ChordTable::getInstance();
	const int cur_chord_size = chord_table[selected_arp].size();
	const int range = static_cast<int>(cur_chord_size * m_arpRangeModel.value() * m_arpRepeatsModel.value());
	const int total_range = range * cnphv.size();

	// number of frames that every note should be played
	const f_cnt_t arp_frames = (f_cnt_t)( m_arpTimeModel.value() / 1000.0f * Engine::mixer()->processingSampleRate() );
	const f_cnt_t gated_frames = (f_cnt_t)( m_arpGateModel.value() * arp_frames / 100.0f );

	// used for calculating remaining frames for arp-note, we have to add
	// arp_frames-1, otherwise the first arp-note will not be setup
	// correctly... -> arp_frames frames silence at the start of every note!
	int cur_frame = ( ( m_arpModeModel.value() != FreeMode ) ?
						cnphv.first()->totalFramesPlayed() :
						_n->totalFramesPlayed() ) + arp_frames - 1;
	// used for loop
	f_cnt_t frames_processed = ( m_arpModeModel.value() != FreeMode ) ? cnphv.first()->noteOffset() : _n->noteOffset();

	while( frames_processed < Engine::mixer()->framesPerPeriod() )
	{
		const f_cnt_t remaining_frames_for_cur_arp = arp_frames - ( cur_frame % arp_frames );
		// does current arp-note fill whole audio-buffer or is the remaining time just
		// a short bit that we can discard?
		if( remaining_frames_for_cur_arp > Engine::mixer()->framesPerPeriod() ||
			_n->frames() - _n->totalFramesPlayed() < arp_frames / 5 )
		{
			// then we don't have to do something!
			break;
		}

		frames_processed += remaining_frames_for_cur_arp;

		// in sorted mode: is it our turn or do we have to be quiet for
		// now?
		if( m_arpModeModel.value() == SortMode &&
				( ( cur_frame / arp_frames ) % total_range ) / range != (f_cnt_t) _n->index() )
		{
			// update counters
			frames_processed += arp_frames;
			cur_frame += arp_frames;
			continue;
		}

		// Skip notes randomly
		if( m_arpSkipModel.value() )
		{
			if( 100 * ( (float) rand() / (float)( RAND_MAX + 1.0f ) ) < m_arpSkipModel.value() )
			{
				// update counters
				frames_processed += arp_frames;
				cur_frame += arp_frames;
				continue;
			}
		}

		int dir = m_arpDirectionModel.value();

		// Miss notes randomly. We intercept int dir and abuse it
		// after need.  :)

		if( m_arpMissModel.value() )
		{
			if( 100 * ( (float) rand() / (float)( RAND_MAX + 1.0f ) ) < m_arpMissModel.value() )
			{
				dir = ArpDirRandom;
			}
		}

		int cur_arp_idx = 0;
		// process according to arpeggio-direction...
		if( dir == ArpDirUp )
		{
			cur_arp_idx = ( cur_frame / arp_frames ) % range;
		}
		else if( dir == ArpDirDown )
		{
			cur_arp_idx = range - ( cur_frame / arp_frames ) %
								range - 1;
		}
		else if( dir == ArpDirUpAndDown && range > 1 )
		{
			// imagine, we had to play the arp once up and then
			// once down -> makes 2 * range possible notes...
			// because we don't play the lower and upper notes
			// twice, we have to subtract 2
			cur_arp_idx = ( cur_frame / arp_frames ) % ( range * 2 - 2 );
			// if greater than range, we have to play down...
			// looks like the code for arp_dir==DOWN... :)
			if( cur_arp_idx >= range )
			{
				cur_arp_idx = range - cur_arp_idx % ( range - 1 ) - 1;
			}
		}
		else if( dir == ArpDirDownAndUp && range > 1 )
		{
			// copied from ArpDirUpAndDown above
			cur_arp_idx = ( cur_frame / arp_frames ) % ( range * 2 - 2 );
			// if greater than range, we have to play down...
			// looks like the code for arp_dir==DOWN... :)
			if( cur_arp_idx >= range )
			{
				cur_arp_idx = range - cur_arp_idx % ( range - 1 ) - 1;
			}
			// inverts direction
			cur_arp_idx = range - cur_arp_idx - 1;
		}
		else if( dir == ArpDirRandom )
		{
			// just pick a random chord-index
			cur_arp_idx = (int)( range * ( (float) rand() / (float) RAND_MAX ) );
		}

		// Divide cur_arp_idx with wanted repeats. The repeat feature will not affect random notes.
		cur_arp_idx = static_cast<int>(cur_arp_idx / m_arpRepeatsModel.value());

		// Cycle notes
		if( m_arpCycleModel.value() && dir != ArpDirRandom )
		{
			cur_arp_idx *= m_arpCycleModel.value() + 1;
			cur_arp_idx %= static_cast<int>( range / m_arpRepeatsModel.value() );
		}

		// now calculate final key for our arp-note
		const int sub_note_key = base_note_key + (cur_arp_idx / cur_chord_size ) *
							KeysPerOctave + chord_table[selected_arp][cur_arp_idx % cur_chord_size];

		// range-checking
		if( sub_note_key >= NumKeys ||
			sub_note_key < 0 ||
			Engine::mixer()->criticalXRuns() )
		{
			continue;
		}

		// create new arp-note

		// create sub-note-play-handle, only ptr to note is different
		// and is_arp_note=true
		Engine::mixer()->addPlayHandle(
				NotePlayHandleManager::acquire( _n->instrumentTrack(),
							frames_processed,
							gated_frames,
							Note( TimePos( 0 ), TimePos( 0 ), sub_note_key, _n->getVolume(),
									_n->getPanning(), _n->detuning() ),
							_n, -1, NotePlayHandle::OriginArpeggio )
				);

		// update counters
		frames_processed += arp_frames;
		cur_frame += arp_frames;
	}
}

void InstrumentFunctionArpeggio::saveSettings( QDomDocument & _doc, QDomElement & _this )
{
	m_arpEnabledModel.saveSettings( _doc, _this, "arp-enabled" );
	m_arpModel.saveSettings( _doc, _this, "arp" );
	m_arpRangeModel.saveSettings( _doc, _this, "arprange" );
	m_arpRepeatsModel.saveSettings( _doc, _this, "arprepeats" );
	m_arpCycleModel.saveSettings( _doc, _this, "arpcycle" );
	m_arpSkipModel.saveSettings( _doc, _this, "arpskip" );
	m_arpMissModel.saveSettings( _doc, _this, "arpmiss" );
	m_arpTimeModel.saveSettings( _doc, _this, "arptime" );
	m_arpGateModel.saveSettings( _doc, _this, "arpgate" );
	m_arpDirectionModel.saveSettings( _doc, _this, "arpdir" );
	m_arpModeModel.saveSettings( _doc, _this, "arpmode" );
}

void InstrumentFunctionArpeggio::loadSettings( const QDomElement & _this )
{
	m_arpEnabledModel.loadSettings( _this, "arp-enabled" );
	m_arpModel.loadSettings( _this, "arp" );
	m_arpRangeModel.loadSettings( _this, "arprange" );
	m_arpRepeatsModel.loadSettings( _this, "arprepeats" );
	m_arpCycleModel.loadSettings( _this, "arpcycle" );
	m_arpSkipModel.loadSettings( _this, "arpskip" );
	m_arpMissModel.loadSettings( _this, "arpmiss" );
	m_arpTimeModel.loadSettings( _this, "arptime" );
	m_arpGateModel.loadSettings( _this, "arpgate" );
	m_arpDirectionModel.loadSettings( _this, "arpdir" );
	m_arpModeModel.loadSettings( _this, "arpmode" );
}


code:
/*
 * InstrumentPlayHandle.cpp - play-handle for driving an instrument
 *
 * Copyright (c) 2005-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
 *
 * This file is part of LMMS - https://lmms.io
 *
 * 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.
 *
 */

#include "InstrumentPlayHandle.h"
#include "InstrumentTrack.h"

InstrumentPlayHandle::InstrumentPlayHandle( Instrument * instrument, InstrumentTrack* instrumentTrack ) :
		PlayHandle( TypeInstrumentPlayHandle ),
		m_instrument( instrument )
{
	setAudioPort( instrumentTrack->audioPort() );
}


code:
/*
 * InstrumentSoundShaping.cpp - implementation of class InstrumentSoundShaping
 *
 * Copyright (c) 2004-2009 Tobias Doerffel <tobydox/at/users.sourceforge.net>
 *
 * This file is part of LMMS - https://lmms.io
 *
 * 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.
 *
 */

#include <QtCore/QVarLengthArray>
#include <QDomElement>

#include "InstrumentSoundShaping.h"
#include "BasicFilters.h"
#include "embed.h"
#include "Engine.h"
#include "EnvelopeAndLfoParameters.h"
#include "Instrument.h"
#include "InstrumentTrack.h"
#include "Mixer.h"

const float CUT_FREQ_MULTIPLIER = 6000.0f;
const float RES_MULTIPLIER = 2.0f;
const float RES_PRECISION = 1000.0f;

// names for env- and lfo-targets - first is name being displayed to user
// and second one is used internally, e.g. for saving/restoring settings
const char *const InstrumentSoundShaping::targetNames[InstrumentSoundShaping::NumTargets][3] =
{
	{ QT_TRANSLATE_NOOP("InstrumentSoundShaping", "VOLUME"), "vol",
			QT_TRANSLATE_NOOP("InstrumentSoundShaping", "Volume") },
	{ QT_TRANSLATE_NOOP("InstrumentSoundShaping", "CUTOFF"), "cut",
			QT_TRANSLATE_NOOP("InstrumentSoundShaping", "Cutoff frequency") },
	{ QT_TRANSLATE_NOOP("InstrumentSoundShaping", "RESO"), "res",
			QT_TRANSLATE_NOOP("InstrumentSoundShaping", "Resonance") }
} ;

InstrumentSoundShaping::InstrumentSoundShaping(
					InstrumentTrack * _instrument_track ) :
	Model( _instrument_track, tr( "Envelopes/LFOs" ) ),
	m_instrumentTrack( _instrument_track ),
	m_filterEnabledModel( false, this ),
	m_filterModel( this, tr( "Filter type" ) ),
	m_filterCutModel( 14000.0, 1.0, 14000.0, 1.0, this, tr( "Cutoff frequency" ) ),
	m_filterResModel( 0.5, BasicFilters<>::minQ(), 10.0, 0.01, this, tr( "Q/Resonance" ) )
{
	for( int i = 0; i < NumTargets; ++i )
	{
		float value_for_zero_amount = 0.0;
		if( i == Volume )
		{
			value_for_zero_amount = 1.0;
		}
		m_envLfoParameters[i] = new EnvelopeAndLfoParameters(
										value_for_zero_amount,
										this );
		m_envLfoParameters[i]->setDisplayName(
			tr( targetNames[i][2] ) );
	}

	m_filterModel.addItem( tr( "Low-pass" ), std::make_unique<PixmapLoader>( "filter_lp" ) );
	m_filterModel.addItem( tr( "Hi-pass" ), std::make_unique<PixmapLoader>( "filter_hp" ) );
	m_filterModel.addItem( tr( "Band-pass csg" ), std::make_unique<PixmapLoader>( "filter_bp" ) );
	m_filterModel.addItem( tr( "Band-pass czpg" ), std::make_unique<PixmapLoader>( "filter_bp" ) );
	m_filterModel.addItem( tr( "Notch" ), std::make_unique<PixmapLoader>( "filter_notch" ) );
	m_filterModel.addItem( tr( "All-pass" ), std::make_unique<PixmapLoader>( "filter_ap" ) );
	m_filterModel.addItem( tr( "Moog" ), std::make_unique<PixmapLoader>( "filter_lp" ) );
	m_filterModel.addItem( tr( "2x Low-pass" ), std::make_unique<PixmapLoader>( "filter_2lp" ) );
	m_filterModel.addItem( tr( "RC Low-pass 12 dB/oct" ), std::make_unique<PixmapLoader>( "filter_lp" ) );
	m_filterModel.addItem( tr( "RC Band-pass 12 dB/oct" ), std::make_unique<PixmapLoader>( "filter_bp" ) );
	m_filterModel.addItem( tr( "RC High-pass 12 dB/oct" ), std::make_unique<PixmapLoader>( "filter_hp" ) );
	m_filterModel.addItem( tr( "RC Low-pass 24 dB/oct" ), std::make_unique<PixmapLoader>( "filter_lp" ) );
	m_filterModel.addItem( tr( "RC Band-pass 24 dB/oct" ), std::make_unique<PixmapLoader>( "filter_bp" ) );
	m_filterModel.addItem( tr( "RC High-pass 24 dB/oct" ), std::make_unique<PixmapLoader>( "filter_hp" ) );
	m_filterModel.addItem( tr( "Vocal Formant" ), std::make_unique<PixmapLoader>( "filter_hp" ) );
	m_filterModel.addItem( tr( "2x Moog" ), std::make_unique<PixmapLoader>( "filter_2lp" ) );
	m_filterModel.addItem( tr( "SV Low-pass" ), std::make_unique<PixmapLoader>( "filter_lp" ) );
	m_filterModel.addItem( tr( "SV Band-pass" ), std::make_unique<PixmapLoader>( "filter_bp" ) );
	m_filterModel.addItem( tr( "SV High-pass" ), std::make_unique<PixmapLoader>( "filter_hp" ) );
	m_filterModel.addItem( tr( "SV Notch" ), std::make_unique<PixmapLoader>( "filter_notch" ) );
	m_filterModel.addItem( tr( "Fast Formant" ), std::make_unique<PixmapLoader>( "filter_hp" ) );
	m_filterModel.addItem( tr( "Tripole" ), std::make_unique<PixmapLoader>( "filter_lp" ) );
}

InstrumentSoundShaping::~InstrumentSoundShaping()
{
}

float InstrumentSoundShaping::volumeLevel( NotePlayHandle* n, const f_cnt_t frame )
{
	f_cnt_t envReleaseBegin = frame - n->releaseFramesDone() + n->framesBeforeRelease();

	if( n->isReleased() == false )
	{
		envReleaseBegin += Engine::mixer()->framesPerPeriod();
	}

	float level;
	m_envLfoParameters[Volume]->fillLevel( &level, frame, envReleaseBegin, 1 );

	return level;
}

void InstrumentSoundShaping::processAudioBuffer( sampleFrame* buffer,
							const fpp_t frames,
							NotePlayHandle* n )
{
	const f_cnt_t envTotalFrames = n->totalFramesPlayed();
	f_cnt_t envReleaseBegin = envTotalFrames - n->releaseFramesDone() + n->framesBeforeRelease();

	if( !n->isReleased() || ( n->instrumentTrack()->isSustainPedalPressed() &&
		!n->isReleaseStarted() ) )
	{
		envReleaseBegin += frames;
	}

	// because of optimizations, there's special code for several cases:
	// 	- cut- and res-lfo/envelope active
	// 	- cut-lfo/envelope active
	// 	- res-lfo/envelope active
	//	- no lfo/envelope active but filter is used

	// only use filter, if it is really needed

	if( m_filterEnabledModel.value() )
	{
		QVarLengthArray<float> cutBuffer(frames);
		QVarLengthArray<float> resBuffer(frames);

		int old_filter_cut = 0;
		int old_filter_res = 0;

		if( n->m_filter == nullptr )
		{
			n->m_filter = std::make_unique<BasicFilters<>>( Engine::mixer()->processingSampleRate() );
		}
		n->m_filter->setFilterType( m_filterModel.value() );

		if( m_envLfoParameters[Cut]->isUsed() )
		{
			m_envLfoParameters[Cut]->fillLevel( cutBuffer.data(), envTotalFrames, envReleaseBegin, frames );
		}
		if( m_envLfoParameters[Resonance]->isUsed() )
		{
			m_envLfoParameters[Resonance]->fillLevel( resBuffer.data(), envTotalFrames, envReleaseBegin, frames );
		}

		const float fcv = m_filterCutModel.value();
		const float frv = m_filterResModel.value();

		if( m_envLfoParameters[Cut]->isUsed() &&
			m_envLfoParameters[Resonance]->isUsed() )
		{
			for( fpp_t frame = 0; frame < frames; ++frame )
			{
				const float new_cut_val = EnvelopeAndLfoParameters::expKnobVal( cutBuffer[frame] ) *
								CUT_FREQ_MULTIPLIER + fcv;

				const float new_res_val = frv + RES_MULTIPLIER * resBuffer[frame];

				if( static_cast<int>( new_cut_val ) != old_filter_cut ||
					static_cast<int>( new_res_val*RES_PRECISION ) != old_filter_res )
				{
					n->m_filter->calcFilterCoeffs( new_cut_val, new_res_val );
					old_filter_cut = static_cast<int>( new_cut_val );
					old_filter_res = static_cast<int>( new_res_val*RES_PRECISION );
				}

				buffer[frame][0] = n->m_filter->update( buffer[frame][0], 0 );
				buffer[frame][1] = n->m_filter->update( buffer[frame][1], 1 );
			}
		}
		else if( m_envLfoParameters[Cut]->isUsed() )
		{
			for( fpp_t frame = 0; frame < frames; ++frame )
			{
				float new_cut_val = EnvelopeAndLfoParameters::expKnobVal( cutBuffer[frame] ) *
								CUT_FREQ_MULTIPLIER + fcv;

				if( static_cast<int>( new_cut_val ) != old_filter_cut )
				{
					n->m_filter->calcFilterCoeffs( new_cut_val, frv );
					old_filter_cut = static_cast<int>( new_cut_val );
				}

				buffer[frame][0] = n->m_filter->update( buffer[frame][0], 0 );
				buffer[frame][1] = n->m_filter->update( buffer[frame][1], 1 );
			}
		}
		else if( m_envLfoParameters[Resonance]->isUsed() )
		{
			for( fpp_t frame = 0; frame < frames; ++frame )
			{
				float new_res_val = frv + RES_MULTIPLIER * resBuffer[frame];

				if( static_cast<int>( new_res_val*RES_PRECISION ) != old_filter_res )
				{
					n->m_filter->calcFilterCoeffs( fcv, new_res_val );
					old_filter_res = static_cast<int>( new_res_val*RES_PRECISION );
				}

				buffer[frame][0] = n->m_filter->update( buffer[frame][0], 0 );
				buffer[frame][1] = n->m_filter->update( buffer[frame][1], 1 );
			}
		}
		else
		{
			n->m_filter->calcFilterCoeffs( fcv, frv );

			for( fpp_t frame = 0; frame < frames; ++frame )
			{
				buffer[frame][0] = n->m_filter->update( buffer[frame][0], 0 );
				buffer[frame][1] = n->m_filter->update( buffer[frame][1], 1 );
			}
		}
	}

	if( m_envLfoParameters[Volume]->isUsed() )
	{
		QVarLengthArray<float> volBuffer(frames);
		m_envLfoParameters[Volume]->fillLevel( volBuffer.data(), envTotalFrames, envReleaseBegin, frames );

		for( fpp_t frame = 0; frame < frames; ++frame )
		{
			float vol_level = volBuffer[frame];
			vol_level = vol_level * vol_level;
			buffer[frame][0] = vol_level * buffer[frame][0];
			buffer[frame][1] = vol_level * buffer[frame][1];
		}
	}

/*	else if( m_envLfoParameters[Volume]->isUsed() == false && m_envLfoParameters[PANNING]->isUsed() )
	{
		// only use panning-envelope...
		for( fpp_t frame = 0; frame < frames; ++frame )
		{
			float vol_level = pan_buf[frame];
			vol_level = vol_level*vol_level;
			for( ch_cnt_t chnl = 0; chnl < DEFAULT_CHANNELS; ++chnl )
			{
				buffer[frame][chnl] = vol_level * buffer[frame][chnl];
			}
		}
	}*/
}

f_cnt_t InstrumentSoundShaping::envFrames( const bool _only_vol ) const
{
	f_cnt_t ret_val = m_envLfoParameters[Volume]->PAHD_Frames();

	if( _only_vol == false )
	{
		for( int i = Volume+1; i < NumTargets; ++i )
		{
			if( m_envLfoParameters[i]->isUsed() &&
				m_envLfoParameters[i]->PAHD_Frames() > ret_val )
			{
				ret_val = m_envLfoParameters[i]->PAHD_Frames();
			}
		}
	}
	return ret_val;
}

f_cnt_t InstrumentSoundShaping::releaseFrames() const
{
	if( !m_instrumentTrack->instrument() )
	{
		return 0;
	}

	f_cnt_t ret_val = m_instrumentTrack->instrument()->desiredReleaseFrames();

	if( m_instrumentTrack->instrument()->flags().testFlag( Instrument::IsSingleStreamed ) )
	{
		return ret_val;
	}

	if( m_envLfoParameters[Volume]->isUsed() )
	{
		return m_envLfoParameters[Volume]->releaseFrames();
	}

	for( int i = Volume+1; i < NumTargets; ++i )
	{
		if( m_envLfoParameters[i]->isUsed() )
		{
			ret_val = qMax( ret_val, m_envLfoParameters[i]->releaseFrames() );
		}
	}
	return ret_val;
}

void InstrumentSoundShaping::saveSettings( QDomDocument & _doc, QDomElement & _this )
{
	m_filterModel.saveSettings( _doc, _this, "ftype" );
	m_filterCutModel.saveSettings( _doc, _this, "fcut" );
	m_filterResModel.saveSettings( _doc, _this, "fres" );
	m_filterEnabledModel.saveSettings( _doc, _this, "fwet" );

	for( int i = 0; i < NumTargets; ++i )
	{
		m_envLfoParameters[i]->saveState( _doc, _this ).setTagName(
			m_envLfoParameters[i]->nodeName() +
				QString( targetNames[i][1] ).toLower() );
	}
}

void InstrumentSoundShaping::loadSettings( const QDomElement & _this )
{
	m_filterModel.loadSettings( _this, "ftype" );
	m_filterCutModel.loadSettings( _this, "fcut" );
	m_filterResModel.loadSettings( _this, "fres" );
	m_filterEnabledModel.loadSettings( _this, "fwet" );

	QDomNode node = _this.firstChild();
	while( !node.isNull() )
	{
		if( node.isElement() )
		{
			for( int i = 0; i < NumTargets; ++i )
			{
				if( node.nodeName() ==
					m_envLfoParameters[i]->nodeName() +
					QString( targetNames[i][1] ).
								toLower() )
				{
					m_envLfoParameters[i]->restoreState( node.toElement() );
				}
			}
		}
		node = node.nextSibling();
	}
}


code:
/*
 * JournallingObject.cpp - implementation of journalling-object related stuff
 *
 * Copyright (c) 2006-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
 *
 * This file is part of LMMS - https://lmms.io
 *
 * 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.
 *
 */

#include <QDomElement>

#include "JournallingObject.h"
#include "AutomatableModel.h"
#include "ProjectJournal.h"
#include "Engine.h"

JournallingObject::JournallingObject() :
	SerializingObject(),
	m_id( Engine::projectJournal()->allocID( this ) ),
	m_journalling( true ),
	m_journallingStateStack()
{
}

JournallingObject::~JournallingObject()
{
	if( Engine::projectJournal() )
	{
		Engine::projectJournal()->freeID( id() );
	}
}

void JournallingObject::addJournalCheckPoint()
{
	if( isJournalling() )
	{
		Engine::projectJournal()->addJournalCheckPoint( this );
	}
}

QDomElement JournallingObject::saveState( QDomDocument & _doc,
							QDomElement & _parent )
{
	if( isJournalling() )
	{
		QDomElement _this = SerializingObject::saveState( _doc, _parent );

		QDomElement journalNode = _doc.createElement( "journallingObject" );
		journalNode.setAttribute( "id", id() );
		journalNode.setAttribute( "metadata", true );
		_this.appendChild( journalNode );

		return _this;
	} else {
		return QDomElement();
	}
}

void JournallingObject::restoreState( const QDomElement & _this )
{
	SerializingObject::restoreState( _this );

	saveJournallingState( false );

	// search for journal-node
	QDomNode node = _this.firstChild();
	while( !node.isNull() )
	{
		if( node.isElement() && node.nodeName() == "journal" )
		{
			const jo_id_t new_id = node.toElement().attribute( "id" ).toInt();
			if( new_id )
			{
				changeID( new_id );
			}
		}
		node = node.nextSibling();
	}

	restoreJournallingState();
}

void JournallingObject::changeID( jo_id_t _id )
{
	if( id() != _id )
	{
		JournallingObject * jo = Engine::projectJournal()->
											journallingObject( _id );
		if( jo != NULL )
		{
			QString used_by = jo->nodeName();
			if( used_by == "automatablemodel" &&
				dynamic_cast<AutomatableModel *>( jo ) )
			{
				used_by += ":" +
					dynamic_cast<AutomatableModel *>( jo )->
								displayName();
			}
			fprintf( stderr, "JO-ID %d already in use by %s!\n",
				(int) _id, used_by.toUtf8().constData() );
			return;
		}

		Engine::projectJournal()->freeID( m_id );
		Engine::projectJournal()->reallocID( _id, this );
		m_id = _id;
	}
}


code:
/*
 * Ladspa2LMMS.cpp - class that identifies and instantiates LADSPA effects
 *                     for use with LMMS
 *
 * Copyright (c) 2005-2008 Danny McRae <khjklujn@netscape.net>
 *
 * This file is part of LMMS - https://lmms.io
 *
 * 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.
 *
 */

#include "Ladspa2LMMS.h"

Ladspa2LMMS::Ladspa2LMMS()
{
	l_sortable_plugin_t plugins = getSortedPlugins();

	for( l_sortable_plugin_t::iterator it = plugins.begin();
			it != plugins.end(); ++it )
	{
		ladspa_key_t key = (*it).second;
		ladspaManagerDescription * desc = getDescription( key );

		if( desc->type == SOURCE )
		{
			m_instruments.append( qMakePair( getName( key ),
								key ) );
		}
		else if( desc->type == TRANSFER &&
			( desc->inputChannels == desc->outputChannels &&
			( desc->inputChannels == 1 ||
			desc->inputChannels == 2 ||
			desc->inputChannels == 4 )/* &&
			isRealTimeCapable( key )*/ ) )
		{
			m_validEffects.append( qMakePair( getName( key ),
								key ) );
		}
		else if( desc->type == TRANSFER &&
			( desc->inputChannels != desc->outputChannels ||
			( desc->inputChannels != 1 &&
			desc->inputChannels != 2 &&
			desc->inputChannels != 4 ) ||
			!isRealTimeCapable( key ) ) )
		{
			m_invalidEffects.append( qMakePair( getName( key ),
								key ) );
		}
		else if( desc->type == SINK )
		{
			m_analysisTools.append( qMakePair( getName( key ),
								key ) );
		}
		else if( desc->type == OTHER )
		{
			m_otherPlugins.append( qMakePair( getName( key ),
								key ) );
		}
	}
}

Ladspa2LMMS::~Ladspa2LMMS()
{
}

QString Ladspa2LMMS::getShortName( const ladspa_key_t & _key )
{
	QString name = getName( _key );

	if( name.indexOf( "(" ) > 0 )
	{
		name = name.left( name.indexOf( "(" ) );
	}
	if( name.indexOf( " - " ) > 0 )
	{
		name = name.left( name.indexOf( " - " ) );
	}
	if( name.indexOf( "  " ) > 0 )
	{
		name = name.left( name.indexOf( "  " ) );
	}
	Qt::CaseSensitivity cs = Qt::CaseInsensitive;
	if( name.indexOf( " with ", 0, cs ) > 0 )
	{
		name = name.left( name.indexOf( " with ", 0, cs ) );
	}
	if( name.indexOf( ",", 0, cs ) > 0 )
	{
		name = name.left( name.indexOf( ",", 0, cs ) );
	}
	if( name.length() > 40 )
	{
		int i = 40;
		while( name[i] != ' ' && i > 0 )
		{
			i--;
		}
		name = name.left( i );
	}
	if( name.length() == 0 )
	{
		name = "LADSPA Plugin";
	}

	return name;
}