code:
/*
 * LadspaControl.cpp - model for controlling a LADSPA port
 *
 * Copyright (c) 2008-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
 * Copyright (c) 2006-2008 Danny McRae <khjklujn/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 <cstdio>

#include "LadspaControl.h"
#include "LadspaBase.h"

LadspaControl::LadspaControl( Model * _parent, port_desc_t * _port,
								bool _link ) :
	Model( _parent ),
	m_link( _link ),
	m_port( _port ),
	m_linkEnabledModel( _link, this, tr( "Link channels" ) ),
	m_toggledModel( false, this, m_port->name ),
	m_knobModel( 0, 0, 0, 1, this, m_port->name ),
	m_tempoSyncKnobModel( 0, 0, 0, 1, m_port->max, this, m_port->name )
{
	if( m_link )
	{
		connect( &m_linkEnabledModel, SIGNAL( dataChanged() ),
					 this, SLOT( linkStateChanged() ),
					 Qt::DirectConnection );
	}

	switch( m_port->data_type )
	{
		case TOGGLED:
			m_toggledModel.setInitValue(
				static_cast<bool>( m_port->def ) );
			connect( &m_toggledModel, SIGNAL( dataChanged() ),
					 this, SLOT( ledChanged() ) );
			if( m_port->def == 1.0f )
			{
				m_toggledModel.setValue( true );
			}
			// TODO: careful: we must prevent saved scales
			m_toggledModel.setScaleLogarithmic( m_port->suggests_logscale );
			break;

		case INTEGER:
		case ENUM:
			m_knobModel.setRange( static_cast<int>( m_port->max ),
					  static_cast<int>( m_port->min ),
					  1 + static_cast<int>( m_port->max -
							  m_port->min ) / 400 );
			m_knobModel.setInitValue(
					static_cast<int>( m_port->def ) );
			connect( &m_knobModel, SIGNAL( dataChanged() ),
						 this, SLOT( knobChanged() ) );
			// TODO: careful: we must prevent saved scales
			m_knobModel.setScaleLogarithmic( m_port->suggests_logscale );
			break;

		case FLOATING:
			m_knobModel.setRange( m_port->min, m_port->max,
				( m_port->max - m_port->min )
				/ ( m_port->name.toUpper() == "GAIN"
					&& m_port->max == 10.0f ? 4000.0f :
								( m_port->suggests_logscale ? 8000000.0f : 800000.0f ) ) );
			m_knobModel.setInitValue( m_port->def );
			connect( &m_knobModel, SIGNAL( dataChanged() ),
						 this, SLOT( knobChanged() ) );
			// TODO: careful: we must prevent saved scales
			m_knobModel.setScaleLogarithmic( m_port->suggests_logscale );
			break;

		case TIME:
			m_tempoSyncKnobModel.setRange( m_port->min, m_port->max,
					  ( m_port->max -
						m_port->min ) / 800.0f );
			m_tempoSyncKnobModel.setInitValue( m_port->def );
			connect( &m_tempoSyncKnobModel, SIGNAL( dataChanged() ),
					 this, SLOT( tempoKnobChanged() ) );
			// TODO: careful: we must prevent saved scales
			m_tempoSyncKnobModel.setScaleLogarithmic( m_port->suggests_logscale );
			break;

		default:
			break;
	}
}

LadspaControl::~LadspaControl()
{
}

LADSPA_Data LadspaControl::value()
{
	switch( m_port->data_type )
	{
		case TOGGLED:
			return static_cast<LADSPA_Data>( m_toggledModel.value() );
		case INTEGER:
		case ENUM:
		case FLOATING:
			return static_cast<LADSPA_Data>( m_knobModel.value() );
		case TIME:
			return static_cast<LADSPA_Data>( m_tempoSyncKnobModel.value() );
		default:
			qWarning( "LadspaControl::value(): BAD BAD BAD\n" );
			break;
	}

	return 0;
}

ValueBuffer * LadspaControl::valueBuffer()
{
	switch( m_port->data_type )
	{
		case TOGGLED:
		case INTEGER:
		case ENUM:
			return NULL;
		case FLOATING:
			return m_knobModel.valueBuffer();
		case TIME:
			return m_tempoSyncKnobModel.valueBuffer();
		default:
			qWarning( "LadspaControl::valueBuffer(): BAD BAD BAD\n" );
			break;
	}

	return NULL;
}

void LadspaControl::setValue( LADSPA_Data _value )
{
	switch( m_port->data_type )
	{
		case TOGGLED:
			m_toggledModel.setValue( static_cast<bool>( _value ) );
			break;
		case INTEGER:
		case ENUM:
			m_knobModel.setValue( static_cast<int>( _value ) );
			break;
		case FLOATING:
			m_knobModel.setValue( static_cast<float>( _value ) );
			break;
		case TIME:
			m_tempoSyncKnobModel.setValue( static_cast<float>(
								_value ) );
			break;
		default:
			printf("LadspaControl::setValue BAD BAD BAD\n");
			break;
	}
}

void LadspaControl::saveSettings( QDomDocument& doc,
					   QDomElement& parent,
					   const QString& name )
{
	QDomElement e = doc.createElement( name );

	if( m_link )
	{
		m_linkEnabledModel.saveSettings( doc, e, "link" );
	}
	switch( m_port->data_type )
	{
		case TOGGLED:
			m_toggledModel.saveSettings( doc, e, "data" );
			break;
		case INTEGER:
		case ENUM:
		case FLOATING:
			m_knobModel.saveSettings( doc, e, "data" );
			break;
		case TIME:
			m_tempoSyncKnobModel.saveSettings( doc, e, "data" );
			break;
		default:
			printf("LadspaControl::saveSettings BAD BAD BAD\n");
			break;
	}

	parent.appendChild( e );
}

void LadspaControl::loadSettings( const QDomElement& parent, const QString& name )
{
	QString dataModelName = "data";
	QString linkModelName = "link";
	QDomElement e = parent.namedItem( name ).toElement();

	if(e.isNull())
	{
		// the port exists in the current effect, but not in the
		// savefile => it's a new port, so load the default value
		if( m_link )
			m_linkEnabledModel.setValue(m_linkEnabledModel.initValue());
		switch( m_port->data_type )
		{
			case TOGGLED:
				m_toggledModel.setValue(m_toggledModel.initValue());
				break;
			case INTEGER:
			case ENUM:
			case FLOATING:
				m_knobModel.setValue(m_knobModel.initValue());
				break;
			case TIME:
				m_tempoSyncKnobModel.setValue(m_tempoSyncKnobModel.initValue());
				break;
			default:
				printf("LadspaControl::loadSettings BAD BAD BAD\n");
				break;
		}
	}
	else
	{

		// COMPAT < 1.0.0: detect old data format where there's either no dedicated sub
		// element or there's a direct sub element with automation link information
		if( e.isNull() || e.hasAttribute( "id" ) )
		{
			dataModelName = name;
			linkModelName = name + "link";
			e = parent;
		}

		if( m_link )
		{
			m_linkEnabledModel.loadSettings( e, linkModelName );
		}

		switch( m_port->data_type )
		{
			case TOGGLED:
				m_toggledModel.loadSettings( e, dataModelName );
				break;
			case INTEGER:
			case ENUM:
			case FLOATING:
				m_knobModel.loadSettings( e, dataModelName );
				break;
			case TIME:
				m_tempoSyncKnobModel.loadSettings( e, dataModelName );
				break;
			default:
				printf("LadspaControl::loadSettings BAD BAD BAD\n");
				break;
		}
	}
}

void LadspaControl::linkControls( LadspaControl * _control )
{
	switch( m_port->data_type )
	{
		case TOGGLED:
			BoolModel::linkModels( &m_toggledModel, _control->toggledModel() );
			break;
		case INTEGER:
		case ENUM:
		case FLOATING:
			FloatModel::linkModels( &m_knobModel, _control->knobModel() );
			break;
		case TIME:
			TempoSyncKnobModel::linkModels( &m_tempoSyncKnobModel,
					_control->tempoSyncKnobModel() );
			break;
		default:
			break;
	}
}

void LadspaControl::ledChanged()
{
	emit changed( m_port->port_id, static_cast<LADSPA_Data>(
						m_toggledModel.value() ) );
}

void LadspaControl::knobChanged()
{
	emit changed( m_port->port_id, static_cast<LADSPA_Data>(
						m_knobModel.value() ) );
}

void LadspaControl::tempoKnobChanged()
{
	emit changed( m_port->port_id, static_cast<LADSPA_Data>(
					m_tempoSyncKnobModel.value() ) );
}

void LadspaControl::unlinkControls( LadspaControl * _control )
{
	switch( m_port->data_type )
	{
		case TOGGLED:
			BoolModel::unlinkModels( &m_toggledModel, _control->toggledModel() );
			break;
		case INTEGER:
		case ENUM:
		case FLOATING:
			FloatModel::unlinkModels( &m_knobModel, _control->knobModel() );
			break;
		case TIME:
			TempoSyncKnobModel::unlinkModels( &m_tempoSyncKnobModel,
					_control->tempoSyncKnobModel() );
			break;
		default:
			break;
	}
}

void LadspaControl::linkStateChanged()
{
	emit linkChanged( m_port->control_id, m_linkEnabledModel.value() );
}

void LadspaControl::setLink( bool _state )
{
	m_linkEnabledModel.setValue( _state );
}


code:
/*
 * LadspaManager.cpp - a class to manage loading and instantiation
 *                      of ladspa plugins
 *
 * Copyright (c) 2005-2008 Danny McRae <khjklujn@netscape.net>
 * Copyright (c) 2011-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 <QCoreApplication>
#include <QDebug>
#include <QDir>
#include <QLibrary>

#include <math.h>

#include "ConfigManager.h"
#include "LadspaManager.h"
#include "PluginFactory.h"

LadspaManager::LadspaManager()
{
	// Make sure plugin search paths are set up
	PluginFactory::setupSearchPaths();

	QStringList ladspaDirectories = QString( getenv( "LADSPA_PATH" ) ).
								split( LADSPA_PATH_SEPERATOR );
	ladspaDirectories += ConfigManager::inst()->ladspaDir().split( ',' );

	ladspaDirectories.push_back( "plugins:ladspa" );
#ifndef LMMS_BUILD_WIN32
	ladspaDirectories.push_back( qApp->applicationDirPath() + '/' + LIB_DIR + "ladspa" );
	ladspaDirectories.push_back( "/usr/lib/ladspa" );
	ladspaDirectories.push_back( "/usr/lib64/ladspa" );
	ladspaDirectories.push_back( "/usr/local/lib/ladspa" );
	ladspaDirectories.push_back( "/usr/local/lib64/ladspa" );
	ladspaDirectories.push_back( "/Library/Audio/Plug-Ins/LADSPA" );
#endif

	for( QStringList::iterator it = ladspaDirectories.begin();
			 		   it != ladspaDirectories.end(); ++it )
	{
		// Skip empty entries as QDir will interpret it as the working directory
		if ((*it).isEmpty()) { continue; }
		QDir directory( ( *it ) );
		QFileInfoList list = directory.entryInfoList();
		for( QFileInfoList::iterator file = list.begin();
						file != list.end(); ++file )
		{
			const QFileInfo & f = *file;
			if( !f.isFile() ||
				 f.fileName().right( 3 ).toLower() !=
#ifdef LMMS_BUILD_WIN32
													"dll"
#else
				 									".so"
#endif
								)
			{
				continue;
			}

			QLibrary plugin_lib( f.absoluteFilePath() );

			if( plugin_lib.load() == true )
			{
				LADSPA_Descriptor_Function descriptorFunction =
			( LADSPA_Descriptor_Function ) plugin_lib.resolve(
							"ladspa_descriptor" );
				if( descriptorFunction != NULL )
				{
					addPlugins( descriptorFunction,
							f.fileName() );
				}
			}
			else
			{
				qWarning() << plugin_lib.errorString();
			}
		}
	}

	l_ladspa_key_t keys = m_ladspaManagerMap.keys();
	for( l_ladspa_key_t::iterator it = keys.begin();
			it != keys.end(); ++it )
	{
		m_sortedPlugins.append( qMakePair( getName( *it ), *it ) );
	}
	std::sort( m_sortedPlugins.begin(), m_sortedPlugins.end() );
}

LadspaManager::~LadspaManager()
{
	for( ladspaManagerMapType::iterator it = m_ladspaManagerMap.begin();
					it != m_ladspaManagerMap.end(); ++it )
	{
		delete it.value();
	}
}

ladspaManagerDescription * LadspaManager::getDescription(
						const ladspa_key_t & _plugin )
{
	if( m_ladspaManagerMap.contains( _plugin ) )
	{
		return( m_ladspaManagerMap[_plugin] );
	}
	else
	{
		return( NULL );
	}
}

void LadspaManager::addPlugins(
		LADSPA_Descriptor_Function _descriptor_func,
						const QString & _file )
{
	const LADSPA_Descriptor * descriptor;

	for( long pluginIndex = 0;
		( descriptor = _descriptor_func( pluginIndex ) ) != NULL;
								++pluginIndex )
	{
		ladspa_key_t key( _file, QString( descriptor->Label ) );
		if( m_ladspaManagerMap.contains( key ) )
		{
			continue;
		}

		ladspaManagerDescription * plugIn =
				new ladspaManagerDescription;
		plugIn->descriptorFunction = _descriptor_func;
		plugIn->index = pluginIndex;
		plugIn->inputChannels = getPluginInputs( descriptor );
		plugIn->outputChannels = getPluginOutputs( descriptor );

		if( plugIn->inputChannels == 0 && plugIn->outputChannels > 0 )
		{
			plugIn->type = SOURCE;
		}
		else if( plugIn->inputChannels > 0 &&
				       plugIn->outputChannels > 0 )
		{
			plugIn->type = TRANSFER;
		}
		else if( plugIn->inputChannels > 0 &&
				       plugIn->outputChannels == 0 )
		{
			plugIn->type = SINK;
		}
		else
		{
			plugIn->type = OTHER;
		}

		m_ladspaManagerMap[key] = plugIn;
	}
}

uint16_t LadspaManager::getPluginInputs(
		const LADSPA_Descriptor * _descriptor )
{
	uint16_t inputs = 0;

	for( uint16_t port = 0; port < _descriptor->PortCount; port++ )
	{
		if( LADSPA_IS_PORT_INPUT(
				_descriptor->PortDescriptors[port] ) &&
			LADSPA_IS_PORT_AUDIO(
				_descriptor->PortDescriptors[port] ) )
		{
			QString name = QString(
					_descriptor->PortNames[port] );
			if( name.toUpper().contains( "IN" ) )
			{
				inputs++;
			}
		}
	}
	return inputs;
}

uint16_t LadspaManager::getPluginOutputs(
		const LADSPA_Descriptor * _descriptor )
{
	uint16_t outputs = 0;

	for( uint16_t port = 0; port < _descriptor->PortCount; port++ )
	{
		if( LADSPA_IS_PORT_OUTPUT(
				_descriptor->PortDescriptors[port] ) &&
			LADSPA_IS_PORT_AUDIO(
				_descriptor->PortDescriptors[port] ) )
		{
			QString name = QString(
					_descriptor->PortNames[port] );
			if( name.toUpper().contains( "OUT" ) )
			{
				outputs++;
			}
		}
	}
	return outputs;
}

const LADSPA_PortDescriptor* LadspaManager::getPortDescriptor(const ladspa_key_t &_plugin, uint32_t _port)
{
	const LADSPA_Descriptor * descriptor = getDescriptor( _plugin );
	if( descriptor && _port < getPortCount( _plugin ) )
	{
		return( & descriptor->PortDescriptors[_port] );
	}
	return( NULL );
}

const LADSPA_PortRangeHint *LadspaManager::getPortRangeHint(const ladspa_key_t &_plugin, uint32_t _port)
{
	const LADSPA_Descriptor * descriptor = getDescriptor( _plugin );
	if( descriptor && _port < getPortCount( _plugin ) )
	{
		return( & descriptor->PortRangeHints[_port] );
	}
	return( NULL );
}

l_sortable_plugin_t LadspaManager::getSortedPlugins()
{
	return( m_sortedPlugins );
}

QString LadspaManager::getLabel( const ladspa_key_t & _plugin )
{
	const LADSPA_Descriptor * descriptor = getDescriptor( _plugin );
	return( descriptor ? descriptor->Label : "" );
}

bool LadspaManager::hasRealTimeDependency(
					const ladspa_key_t &  _plugin )
{
	const LADSPA_Descriptor * descriptor = getDescriptor( _plugin );
	return( descriptor ? LADSPA_IS_REALTIME( descriptor->Properties )
					   : false );
}

bool LadspaManager::isInplaceBroken( const ladspa_key_t &  _plugin )
{
	const LADSPA_Descriptor * descriptor = getDescriptor( _plugin );
	return( descriptor ? LADSPA_IS_INPLACE_BROKEN( descriptor->Properties )
					   : false );
}

bool LadspaManager::isRealTimeCapable(
					const ladspa_key_t &  _plugin )
{
	const LADSPA_Descriptor * descriptor = getDescriptor( _plugin );
	return( descriptor ? LADSPA_IS_HARD_RT_CAPABLE( descriptor->Properties )
					   : false );
}

QString LadspaManager::getName( const ladspa_key_t & _plugin )
{
	const LADSPA_Descriptor * descriptor = getDescriptor( _plugin );
	return( descriptor ? descriptor->Name : "" );
}

QString LadspaManager::getMaker( const ladspa_key_t & _plugin )
{
	const LADSPA_Descriptor * descriptor = getDescriptor( _plugin );
	return( descriptor ? descriptor->Maker : "" );
}

QString LadspaManager::getCopyright( const ladspa_key_t & _plugin )
{
	const LADSPA_Descriptor * descriptor = getDescriptor( _plugin );
	return( descriptor ? descriptor->Copyright : "" );
}

uint32_t LadspaManager::getPortCount( const ladspa_key_t & _plugin )
{
	const LADSPA_Descriptor * descriptor = getDescriptor( _plugin );
	return( descriptor ? descriptor->PortCount : 0 );
}

bool LadspaManager::isPortInput( const ladspa_key_t & _plugin,
								uint32_t _port )
{
	const auto * descriptor = getPortDescriptor( _plugin, _port );
	return( descriptor && LADSPA_IS_PORT_INPUT( * descriptor ) );
}

bool LadspaManager::isPortOutput( const ladspa_key_t & _plugin,
								uint32_t _port )
{
	const auto * descriptor = getPortDescriptor( _plugin, _port );
	return( descriptor && LADSPA_IS_PORT_OUTPUT( * descriptor ) );
}

bool LadspaManager::isPortAudio( const ladspa_key_t & _plugin,
								uint32_t _port )
{
	const auto * descriptor = getPortDescriptor( _plugin, _port );
	return( descriptor && LADSPA_IS_PORT_AUDIO( * descriptor ) );
}

bool LadspaManager::isPortControl( const ladspa_key_t & _plugin,
								uint32_t _port )
{
	const auto * descriptor = getPortDescriptor( _plugin, _port );
	return( descriptor && LADSPA_IS_PORT_CONTROL( * descriptor ) );
}

bool LadspaManager::areHintsSampleRateDependent(
						const ladspa_key_t & _plugin,
								uint32_t _port )
{
	const auto* portRangeHint = getPortRangeHint( _plugin, _port );
	return portRangeHint && LADSPA_IS_HINT_SAMPLE_RATE( portRangeHint->HintDescriptor );
}

float LadspaManager::getLowerBound( const ladspa_key_t & _plugin,
								uint32_t _port )
{
	const auto* portRangeHint = getPortRangeHint( _plugin, _port );
	if( portRangeHint && LADSPA_IS_HINT_BOUNDED_BELOW( portRangeHint->HintDescriptor ) )
	{
		return( portRangeHint->LowerBound );
	}
	return( NOHINT );
}

float LadspaManager::getUpperBound( const ladspa_key_t & _plugin,
									uint32_t _port )
{
	const auto* portRangeHint = getPortRangeHint( _plugin, _port );
	if( portRangeHint && LADSPA_IS_HINT_BOUNDED_ABOVE( portRangeHint->HintDescriptor ) )
	{
		return( portRangeHint->UpperBound );
	}
	return( NOHINT );
}

bool LadspaManager::isPortToggled( const ladspa_key_t & _plugin,
								uint32_t _port )
{
	const auto* portRangeHint = getPortRangeHint( _plugin, _port );
	return( portRangeHint && LADSPA_IS_HINT_TOGGLED( portRangeHint->HintDescriptor ) );
}

float LadspaManager::getDefaultSetting( const ladspa_key_t & _plugin,
							uint32_t _port )
{
	const auto* portRangeHint = getPortRangeHint( _plugin, _port );
	if( portRangeHint )
	{
		LADSPA_PortRangeHintDescriptor hintDescriptor = portRangeHint->HintDescriptor;
		switch( hintDescriptor & LADSPA_HINT_DEFAULT_MASK )
		{
			case LADSPA_HINT_DEFAULT_NONE:
				return( NOHINT );
			case LADSPA_HINT_DEFAULT_MINIMUM:
				return( portRangeHint->LowerBound );
			case LADSPA_HINT_DEFAULT_LOW:
				if( LADSPA_IS_HINT_LOGARITHMIC
							( hintDescriptor ) )
				{
					return( exp( log( portRangeHint->LowerBound ) * 0.75 +
								 log( portRangeHint->UpperBound ) * 0.25 ) );
				}
				else
				{
					return( portRangeHint->LowerBound * 0.75 +
							portRangeHint->UpperBound * 0.25 );
				}
			case LADSPA_HINT_DEFAULT_MIDDLE:
				if( LADSPA_IS_HINT_LOGARITHMIC
						( hintDescriptor ) )
				{
					return( sqrt( portRangeHint->LowerBound
								  * portRangeHint->UpperBound ) );
				}
				else
				{
					return( 0.5 * ( portRangeHint->LowerBound
							+ portRangeHint->UpperBound ) );
				}
			case LADSPA_HINT_DEFAULT_HIGH:
				if( LADSPA_IS_HINT_LOGARITHMIC
						( hintDescriptor ) )
				{
					return( exp( log( portRangeHint->LowerBound ) * 0.25 +
								 log( portRangeHint->UpperBound ) * 0.75 ) );
				}
				else
				{
					return( portRangeHint->LowerBound * 0.25 +
							portRangeHint->UpperBound * 0.75 );
				}
			case LADSPA_HINT_DEFAULT_MAXIMUM:
				return( portRangeHint->UpperBound );
			case LADSPA_HINT_DEFAULT_0:
				return( 0.0 );
			case LADSPA_HINT_DEFAULT_1:
				return( 1.0 );
			case LADSPA_HINT_DEFAULT_100:
				return( 100.0 );
			case LADSPA_HINT_DEFAULT_440:
				return( 440.0 );
			default:
				return( NOHINT );
		}
	}
	else
	{
		return( NOHINT );
	}
}

bool LadspaManager::isLogarithmic( const ladspa_key_t & _plugin,
								uint32_t _port )
{
	const auto* portRangeHint = getPortRangeHint( _plugin, _port );
	return( portRangeHint && LADSPA_IS_HINT_LOGARITHMIC( portRangeHint->HintDescriptor ) );
}

bool LadspaManager::isInteger( const ladspa_key_t & _plugin,
								uint32_t _port )
{
	const auto* portRangeHint = getPortRangeHint( _plugin, _port );
	return( portRangeHint && LADSPA_IS_HINT_INTEGER( portRangeHint->HintDescriptor ) );
}

bool LadspaManager::isEnum( const ladspa_key_t & _plugin, uint32_t _port )
{
	if( m_ladspaManagerMap.contains( _plugin )
		   && _port < getPortCount( _plugin ) )
	{
		LADSPA_Descriptor_Function descriptorFunction =
			m_ladspaManagerMap[_plugin]->descriptorFunction;
		const LADSPA_Descriptor * descriptor =
				descriptorFunction(
					m_ladspaManagerMap[_plugin]->index );
		LADSPA_PortRangeHintDescriptor hintDescriptor =
			descriptor->PortRangeHints[_port].HintDescriptor;
		// This is an LMMS extension to ladspa
		return( LADSPA_IS_HINT_INTEGER( hintDescriptor ) &&
			LADSPA_IS_HINT_TOGGLED( hintDescriptor ) );
	}
	else
	{
		return( false );
	}
}

QString LadspaManager::getPortName( const ladspa_key_t & _plugin,
								uint32_t _port )
{
	const LADSPA_Descriptor * descriptor = getDescriptor( _plugin );
	return( descriptor ? descriptor->PortNames[_port] : QString( "" ) );
}

const void * LadspaManager::getImplementationData(
						const ladspa_key_t & _plugin )
{
	const LADSPA_Descriptor * descriptor = getDescriptor( _plugin );
	return( descriptor ? descriptor->ImplementationData : NULL );
}

const LADSPA_Descriptor * LadspaManager::getDescriptor(
						const ladspa_key_t & _plugin )
{
	if( m_ladspaManagerMap.contains( _plugin ) )
	{
		LADSPA_Descriptor_Function descriptorFunction =
			m_ladspaManagerMap[_plugin]->descriptorFunction;
		const LADSPA_Descriptor * descriptor =
				descriptorFunction(
					m_ladspaManagerMap[_plugin]->index );
		return( descriptor );
	}
	else
	{
		return( NULL );
	}
}

LADSPA_Handle LadspaManager::instantiate(
					const ladspa_key_t & _plugin,
							uint32_t _sample_rate )
{
	const LADSPA_Descriptor * descriptor = getDescriptor( _plugin );
	return( descriptor ?
				( descriptor->instantiate )( descriptor, _sample_rate ) :
				NULL );
}

bool LadspaManager::connectPort( const ladspa_key_t & _plugin,
						LADSPA_Handle _instance,
						uint32_t _port,
						LADSPA_Data * _data_location )
{
	const LADSPA_Descriptor * descriptor = getDescriptor( _plugin );
	if( descriptor && descriptor->connect_port != NULL &&
			_port < getPortCount( _plugin ) )
	{
		( descriptor->connect_port )
				( _instance, _port, _data_location );
		return( true );
	}
	return( false );
}

bool LadspaManager::activate( const ladspa_key_t & _plugin,
					LADSPA_Handle _instance )
{
	const LADSPA_Descriptor * descriptor = getDescriptor( _plugin );
	if( descriptor && descriptor->activate != NULL )
	{
		( descriptor->activate ) ( _instance );
		return( true );
	}
	return( false );
}

bool LadspaManager::run( const ladspa_key_t & _plugin,
							LADSPA_Handle _instance,
							uint32_t _sample_count )
{
	const LADSPA_Descriptor * descriptor = getDescriptor( _plugin );
	if( descriptor && descriptor->run!= NULL )
	{
		( descriptor->run ) ( _instance, _sample_count );
		return( true );
	}
	return( false );
}

bool LadspaManager::runAdding( const ladspa_key_t & _plugin,
						LADSPA_Handle _instance,
						uint32_t _sample_count )
{
	const LADSPA_Descriptor * descriptor = getDescriptor( _plugin );
	if( descriptor && descriptor->run_adding!= NULL
			&& descriptor->set_run_adding_gain != NULL )
	{
		( descriptor->run_adding ) ( _instance, _sample_count );
		return( true );
	}
	return( false );
}

bool LadspaManager::setRunAddingGain( const ladspa_key_t & _plugin,
						LADSPA_Handle _instance,
						LADSPA_Data _gain )
{
	const LADSPA_Descriptor * descriptor = getDescriptor( _plugin );
	if( descriptor && descriptor->run_adding!= NULL
			&& descriptor->set_run_adding_gain != NULL )
	{
		( descriptor->set_run_adding_gain ) ( _instance, _gain );
		return( true );
	}
	return( false );
}

bool LadspaManager::deactivate( const ladspa_key_t & _plugin,
						LADSPA_Handle _instance )
{
	const LADSPA_Descriptor * descriptor = getDescriptor( _plugin );
	if( descriptor && descriptor->deactivate!= NULL )
	{
		( descriptor->deactivate ) ( _instance );
		return( true );
	}
	return( false );
}

bool LadspaManager::cleanup( const ladspa_key_t & _plugin,
						LADSPA_Handle _instance )
{
	const LADSPA_Descriptor * descriptor = getDescriptor( _plugin );
	if( descriptor && descriptor->cleanup!= NULL )
	{
		( descriptor->cleanup ) ( _instance );
		return( true );
	}
	return( false );
}

repo_name: CYBERDEViL/LMMS

license: mit


code:
/*
 * LfoController.cpp - implementation of class controller which handles
 *                      remote-control of AutomatableModels
 *
 * Copyright (c) 2008 Paul Giblock <drfaygo/at/gmail.com>
 *
 * 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 <QObject>

#include "Song.h"
#include "Mixer.h"
#include "LfoController.h"

LfoController::LfoController( Model * _parent ) :
	Controller( Controller::LfoController, _parent, tr( "LFO Controller" ) ),
	m_baseModel( 0.5, 0.0, 1.0, 0.001, this, tr( "Base value" ) ),
	m_speedModel( 2.0, 0.01, 20.0, 0.0001, 20000.0, this, tr( "Oscillator speed" ) ),
	m_amountModel( 1.0, -1.0, 1.0, 0.005, this, tr( "Oscillator amount" ) ),
	m_phaseModel( 0.0, 0.0, 360.0, 4.0, this, tr( "Oscillator phase" ) ),
	m_waveModel( Oscillator::SineWave, 0, Oscillator::NumWaveShapes,
			this, tr( "Oscillator waveform" ) ),
	m_multiplierModel( 0, 0, 2, this, tr( "Frequency Multiplier" ) ),
	m_duration( 1000 ),
	m_phaseOffset( 0 ),
	m_currentPhase( 0 ),
	m_sampleFunction( &Oscillator::sinSample ),
	m_userDefSampleBuffer( new SampleBuffer )
{
	setSampleExact( true );
	connect( &m_waveModel, SIGNAL( dataChanged() ),
			this, SLOT( updateSampleFunction() ), Qt::DirectConnection );

	connect( &m_speedModel, SIGNAL( dataChanged() ),
			this, SLOT( updateDuration() ), Qt::DirectConnection );
	connect( &m_multiplierModel, SIGNAL( dataChanged() ),
			this, SLOT( updateDuration() ), Qt::DirectConnection );
	connect( Engine::mixer(), SIGNAL( sampleRateChanged() ),
			this, SLOT( updateDuration() ) );

	connect( Engine::getSong(), SIGNAL( playbackStateChanged() ),
			this, SLOT( updatePhase() ) );
	connect( Engine::getSong(), SIGNAL( playbackPositionChanged() ),
			this, SLOT( updatePhase() ) );

	updateDuration();
}

LfoController::~LfoController()
{
	sharedObject::unref( m_userDefSampleBuffer );
	m_baseModel.disconnect( this );
	m_speedModel.disconnect( this );
	m_amountModel.disconnect( this );
	m_phaseModel.disconnect( this );
	m_waveModel.disconnect( this );
	m_multiplierModel.disconnect( this );
}

void LfoController::updateValueBuffer()
{
	m_phaseOffset = m_phaseModel.value() / 360.0;
	float phase = m_currentPhase + m_phaseOffset;

	// roll phase up until we're in sync with period counter
	m_bufferLastUpdated++;
	if( m_bufferLastUpdated < s_periods )
	{
		int diff = s_periods - m_bufferLastUpdated;
		phase += static_cast<float>( Engine::mixer()->framesPerPeriod() * diff ) / m_duration;
		m_bufferLastUpdated += diff;
	}

	float amount = m_amountModel.value();
	ValueBuffer *amountBuffer = m_amountModel.valueBuffer();
	int amountInc = amountBuffer ? 1 : 0;
	float *amountPtr = amountBuffer ? &(amountBuffer->values()[ 0 ] ) : &amount;

	for( float& f : m_valueBuffer )
	{
		const float currentSample = m_sampleFunction != NULL
			? m_sampleFunction( phase )
			: m_userDefSampleBuffer->userWaveSample( phase );

		f = qBound( 0.0f, m_baseModel.value() + ( *amountPtr * currentSample / 2.0f ), 1.0f );

		phase += 1.0 / m_duration;
		amountPtr += amountInc;
	}

	m_currentPhase = absFraction( phase - m_phaseOffset );
	m_bufferLastUpdated = s_periods;
}

void LfoController::updatePhase()
{
	m_currentPhase = ( Engine::getSong()->getFrames() ) / m_duration;
	m_bufferLastUpdated = s_periods - 1;
}

void LfoController::updateDuration()
{
	float newDurationF = Engine::mixer()->processingSampleRate() *	m_speedModel.value();

	switch(m_multiplierModel.value() )
	{
		case 1:
			newDurationF /= 100.0;
			break;

		case 2:
			newDurationF *= 100.0;
			break;

		default:
			break;
	}

	m_duration = newDurationF;
}

void LfoController::updateSampleFunction()
{
	switch( m_waveModel.value() )
	{
		case Oscillator::SineWave:
			m_sampleFunction = &Oscillator::sinSample;
			break;
		case Oscillator::TriangleWave:
			m_sampleFunction = &Oscillator::triangleSample;
			break;
		case Oscillator::SawWave:
			m_sampleFunction = &Oscillator::sawSample;
			break;
		case Oscillator::SquareWave:
			m_sampleFunction = &Oscillator::squareSample;
			break;
		case Oscillator::MoogSawWave:
			m_sampleFunction = &Oscillator::moogSawSample;
			break;
		case Oscillator::ExponentialWave:
			m_sampleFunction = &Oscillator::expSample;
			break;
		case Oscillator::WhiteNoise:
			m_sampleFunction = &Oscillator::noiseSample;
			break;
		case Oscillator::UserDefinedWave:
			m_sampleFunction = NULL;
			/*TODO: If C++11 is allowed, should change the type of
			 m_sampleFunction be std::function<sample_t(const float)>
			 and use the line below:
			*/
			//m_sampleFunction = &(m_userDefSampleBuffer->userWaveSample)
			break;
	}
}

void LfoController::saveSettings( QDomDocument & _doc, QDomElement & _this )
{
	Controller::saveSettings( _doc, _this );

	m_baseModel.saveSettings( _doc, _this, "base" );
	m_speedModel.saveSettings( _doc, _this, "speed" );
	m_amountModel.saveSettings( _doc, _this, "amount" );
	m_phaseModel.saveSettings( _doc, _this, "phase" );
	m_waveModel.saveSettings( _doc, _this, "wave" );
	m_multiplierModel.saveSettings( _doc, _this, "multiplier" );
	_this.setAttribute( "userwavefile" , m_userDefSampleBuffer->audioFile() );
}

void LfoController::loadSettings( const QDomElement & _this )
{
	Controller::loadSettings( _this );

	m_baseModel.loadSettings( _this, "base" );
	m_speedModel.loadSettings( _this, "speed" );
	m_amountModel.loadSettings( _this, "amount" );
	m_phaseModel.loadSettings( _this, "phase" );
	m_waveModel.loadSettings( _this, "wave" );
	m_multiplierModel.loadSettings( _this, "multiplier" );
	m_userDefSampleBuffer->setAudioFile( _this.attribute("userwavefile" ) );

	updateSampleFunction();
}

QString LfoController::nodeName() const
{
	return( "lfocontroller" );
}

ControllerDialog * LfoController::createDialog( QWidget * _parent )
{
	return new LfoControllerDialog( this, _parent );
}


code:
/*
 * LinkedModelGroups.cpp - base classes for groups of linked models
 *
 * Copyright (c) 2019-2019 Johannes Lorenz <j.git$$$lorenz-ho.me, $$$=@>
 *
 * 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 "LinkedModelGroups.h"

#include <QDomDocument>
#include <QDomElement>

#include "AutomatableModel.h"
#include "stdshims.h"

/*
	LinkedModelGroup
*/

void LinkedModelGroup::linkControls(LinkedModelGroup *other)
{
	foreach_model([&other](const std::string& id, ModelInfo& inf)
	{
		auto itr2 = other->m_models.find(id);
		Q_ASSERT(itr2 != other->m_models.end());
		AutomatableModel::linkModels(inf.m_model, itr2->second.m_model);
	});
}

void LinkedModelGroup::saveValues(QDomDocument &doc, QDomElement &that)
{
	foreach_model([&doc, &that](const std::string& , ModelInfo& inf)
	{
		inf.m_model->saveSettings(doc, that, /*m_models[idx].m_name*/ inf.m_name); /* TODO: m_name useful */
	});
}

void LinkedModelGroup::loadValues(const QDomElement &that)
{
	foreach_model([&that](const std::string& , ModelInfo& inf)
	{
		// try to load, if it fails, this will load a sane initial value
		inf.m_model->loadSettings(that, /*m_models()[idx].m_name*/ inf.m_name); /* TODO: m_name useful */
	});
}

void LinkedModelGroup::addModel(AutomatableModel *model, const QString &name)
{
	model->setObjectName(name);
	m_models.emplace(std::string(name.toUtf8().data()), ModelInfo(name, model));
	connect(model, &AutomatableModel::destroyed,
				this, [this, model](jo_id_t){
					if(containsModel(model->objectName()))
					{
						emit modelRemoved(model);
						eraseModel(model->objectName());
					}
				},
				Qt::DirectConnection);

	// View needs to create another child view, e.g. a new knob:
	emit modelAdded(model);
	emit dataChanged();
}

void LinkedModelGroup::removeControl(AutomatableModel* mdl)
{
	if(containsModel(mdl->objectName()))
	{
		emit modelRemoved(mdl);
		eraseModel(mdl->objectName());
	}
}

bool LinkedModelGroup::eraseModel(const QString& name)
{
	return m_models.erase(name.toStdString()) > 0;
}

void LinkedModelGroup::clearModels()
{
	m_models.clear();
}

bool LinkedModelGroup::containsModel(const QString &name) const
{
	return m_models.find(name.toStdString()) != m_models.end();
}

/*
	LinkedModelGroups
*/

LinkedModelGroups::~LinkedModelGroups() {}

void LinkedModelGroups::linkAllModels()
{
	LinkedModelGroup* first = getGroup(0);
	LinkedModelGroup* cur;

	for (std::size_t i = 1; (cur = getGroup(i)); ++i)
	{
		first->linkControls(cur);
	}
}

void LinkedModelGroups::saveSettings(QDomDocument& doc, QDomElement& that)
{
	LinkedModelGroup* grp0 = getGroup(0);
	if (grp0)
	{
		QDomElement models = doc.createElement("models");
		that.appendChild(models);
		grp0->saveValues(doc, models);
	}
	else { /* don't even add a "models" node */ }
}

void LinkedModelGroups::loadSettings(const QDomElement& that)
{
	QDomElement models = that.firstChildElement("models");
	LinkedModelGroup* grp0;
	if (!models.isNull() && (grp0 = getGroup(0)))
	{
		// only load the first group, the others are linked to the first
		grp0->loadValues(models);
	}
}


code:
/*
 * LocklessAllocator.cpp - allocator with lockless alloc and free
 *
 * Copyright (c) 2016 Javier Serrano Polo <javier@jasp.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 "LocklessAllocator.h"

#include <algorithm>
#include <stdio.h>

#include "lmmsconfig.h"

#ifndef LMMS_BUILD_WIN32
#include <strings.h>
#endif

static const size_t SIZEOF_SET = sizeof( int ) * 8;

static size_t align( size_t size, size_t alignment )
{
	size_t misalignment = size % alignment;
	if( misalignment )
	{
		size += alignment - misalignment;
	}
	return size;
}

LocklessAllocator::LocklessAllocator( size_t nmemb, size_t size )
{
	m_capacity = align( nmemb, SIZEOF_SET );
	m_elementSize = align( size, sizeof( void * ) );
	m_pool = new char[m_capacity * m_elementSize];

	m_freeStateSets = m_capacity / SIZEOF_SET;
	m_freeState = new std::atomic_int[m_freeStateSets];
	std::fill(m_freeState, m_freeState + m_freeStateSets, 0);

	m_available = m_capacity;
	m_startIndex = 0;
}

LocklessAllocator::~LocklessAllocator()
{
	int available = m_available;
	if( available != m_capacity )
	{
		fprintf( stderr, "LocklessAllocator: "
				"Destroying with elements still allocated\n" );
	}

	delete[] m_pool;
	delete[] m_freeState;
}

#ifdef LMMS_BUILD_WIN32
static int ffs( int i )
{
	if( !i )
	{
		return 0;
	}
	for( int j = 0;; )
	{
		if( i & 1 << j++ )
		{
			return j;
		}
	}
}
#endif

void * LocklessAllocator::alloc()
{
	// Some of these CAS loops could probably use relaxed atomics, as discussed
	// in http://en.cppreference.com/w/cpp/atomic/atomic/compare_exchange.
	// Let's use sequentially-consistent ops to be safe for now.
	int available = m_available.load();
	do
	{
		if( !available )
		{
			fprintf( stderr, "LocklessAllocator: No free space\n" );
			return NULL;
		}
	}
	while (!m_available.compare_exchange_weak(available, available - 1));

	const size_t startIndex = m_startIndex++ % m_freeStateSets;
	for (size_t set = startIndex;; set = ( set + 1 ) % m_freeStateSets)
	{
		for (int freeState = m_freeState[set]; freeState != -1;)
		{
			int bit = ffs( ~freeState ) - 1;
			if (m_freeState[set].compare_exchange_weak(freeState,
							freeState | 1 << bit ) )
			{
				return m_pool + ( SIZEOF_SET * set + bit )
								* m_elementSize;
			}
		}
	}
}

void LocklessAllocator::free( void * ptr )
{
	ptrdiff_t diff = (char *)ptr - m_pool;
	if( diff < 0 || diff % m_elementSize )
	{
invalid:
		fprintf( stderr, "LocklessAllocator: Invalid pointer\n" );
		return;
	}
	size_t offset = diff / m_elementSize;
	if( offset >= m_capacity )
	{
		goto invalid;
	}
	size_t set = offset / SIZEOF_SET;
	int bit = offset % SIZEOF_SET;
	int mask = 1 << bit;
	int prevState = m_freeState[set].fetch_and(~mask);
	if ( !( prevState & mask ) )
	{
		fprintf( stderr, "LocklessAllocator: Block not in use\n" );
		return;
	}
	++m_available;
}


code:
/*
 * Copyright (c) 2014 Simon Symeonidis <lethaljellybean/at/gmail/com>
 * Copyright (c) 2008-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 <stdlib.h>

#include "lmms_basics.h"
#include "MemoryHelper.h"

/**
 * Allocate a number of bytes and return them.
 * @param byteNum is the number of bytes
 */
void* MemoryHelper::alignedMalloc( size_t byteNum )
{
	char *ptr, *ptr2, *aligned_ptr;
	int align_mask = ALIGN_SIZE - 1;

	ptr = static_cast<char*>( malloc( byteNum + ALIGN_SIZE + sizeof( int ) ) );

	if( ptr == NULL ) return NULL;

	ptr2 = ptr + sizeof( int );
	aligned_ptr = ptr2 + ( ALIGN_SIZE - ( ( size_t ) ptr2 & align_mask ) );

	ptr2 = aligned_ptr - sizeof( int );
	*( ( int* ) ptr2 ) = ( int )( aligned_ptr - ptr );

	return aligned_ptr;
}

/**
 * Free an aligned buffer
 * @param _buffer is the buffer to free
 */
void MemoryHelper::alignedFree( void* _buffer )
{
	if( _buffer )
	{
		int *ptr2 = static_cast<int*>( _buffer ) - 1;
		_buffer = static_cast<char*>( _buffer ) - *ptr2;
		free( _buffer );
	}
}


code:
/*
 * MemoryManager.cpp
 *
 * Copyright (c) 2017 Lukas W <lukaswhl/at/gmail.com>
 *
 * 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 "MemoryManager.h"

#include <QtCore/QtGlobal>
#include "rpmalloc.h"

/// Global static object handling rpmalloc intializing and finalizing
struct MemoryManagerGlobalGuard {
	MemoryManagerGlobalGuard() {
		rpmalloc_initialize();
	}
	~MemoryManagerGlobalGuard() {
		rpmalloc_finalize();
	}
} static mm_global_guard;

namespace {
static thread_local size_t thread_guard_depth;
}

MemoryManager::ThreadGuard::ThreadGuard()
{
	if (thread_guard_depth++ == 0) {
		rpmalloc_thread_initialize();
	}
}

MemoryManager::ThreadGuard::~ThreadGuard()
{
	if (--thread_guard_depth == 0) {
		rpmalloc_thread_finalize();
	}
}

static thread_local MemoryManager::ThreadGuard local_mm_thread_guard{};

void* MemoryManager::alloc(size_t size)
{
	// Reference local thread guard to ensure it is initialized.
	// Compilers may optimize the instance away otherwise.
	Q_UNUSED(&local_mm_thread_guard);
	Q_ASSERT_X(rpmalloc_is_thread_initialized(), "MemoryManager::alloc", "Thread not initialized");
	return rpmalloc(size);
}

void MemoryManager::free(void * ptr)
{
	Q_UNUSED(&local_mm_thread_guard);
	Q_ASSERT_X(rpmalloc_is_thread_initialized(), "MemoryManager::free", "Thread not initialized");
	return rpfree(ptr);
}


code:
/*
 * MeterModel.cpp - model for meter specification
 *
 * 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 "MeterModel.h"
#include "AutomationPattern.h"

MeterModel::MeterModel( ::Model * _parent ) :
	Model( _parent ),
	m_numeratorModel( 4, 1, 32, this, tr( "Numerator" ) ),
	m_denominatorModel( 4, 1, 32, this, tr( "Denominator" ) )
{
	connect( &m_numeratorModel, SIGNAL( dataChanged() ),
			this, SIGNAL( dataChanged() ), Qt::DirectConnection );
	connect( &m_denominatorModel, SIGNAL( dataChanged() ),
			this, SIGNAL( dataChanged() ), Qt::DirectConnection );
}

MeterModel::~MeterModel()
{
}

void MeterModel::reset()
{
	m_numeratorModel.setValue( 4 );
	m_denominatorModel.setValue( 4 );

	AutomationPattern::globalAutomationPattern( &m_numeratorModel )->clear();
	AutomationPattern::globalAutomationPattern( &m_denominatorModel )->clear();
}

void MeterModel::saveSettings( QDomDocument & _doc, QDomElement & _this,
								const QString & _name )
{
	m_numeratorModel.saveSettings( _doc, _this, _name + "_numerator" );
	m_denominatorModel.saveSettings( _doc, _this, _name + "_denominator" );
}

void MeterModel::loadSettings( const QDomElement & _this,
								const QString & _name )
{
	m_numeratorModel.loadSettings( _this, _name + "_numerator" );
	m_denominatorModel.loadSettings( _this, _name + "_denominator" );
}


code:
#include "MicroTimer.h"

using namespace std;
using namespace std::chrono;

static_assert(ratio_less_equal<steady_clock::duration::period, micro>::value,
	"MicroTimer: steady_clock doesn't support microsecond resolution");

MicroTimer::MicroTimer()
{
	reset();
}

MicroTimer::~MicroTimer()
{
}

void MicroTimer::reset()
{
	begin = steady_clock::now();
}

int MicroTimer::elapsed() const
{
	auto now = steady_clock::now();
	return std::chrono::duration_cast<std::chrono::duration<int, std::micro>>(now - begin).count();
}


code:
/*
 * MixHelpers.cpp - helper functions for mixing buffers
 *
 * Copyright (c) 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 "MixHelpers.h"

#include <cstdio>

#include "lmms_math.h"
#include "ValueBuffer.h"

#include <cstdio>

static bool s_NaNHandler;

namespace MixHelpers
{

/*! \brief Function for applying MIXOP on all sample frames */
template<typename MIXOP>
static inline void run( sampleFrame* dst, const sampleFrame* src, int frames, const MIXOP& OP )
{
	for( int i = 0; i < frames; ++i )
	{
		OP( dst[i], src[i] );
	}
}

/*! \brief Function for applying MIXOP on all sample frames - split source */
template<typename MIXOP>
static inline void run( sampleFrame* dst, const sample_t* srcLeft, const sample_t* srcRight, int frames, const MIXOP& OP )
{
	for( int i = 0; i < frames; ++i )
	{
		const sampleFrame src = { srcLeft[i], srcRight[i] };
		OP( dst[i], src );
	}
}

bool isSilent( const sampleFrame* src, int frames )
{
	const float silenceThreshold = 0.0000001f;

	for( int i = 0; i < frames; ++i )
	{
		if( fabsf( src[i][0] ) >= silenceThreshold || fabsf( src[i][1] ) >= silenceThreshold )
		{
			return false;
		}
	}

	return true;
}

bool useNaNHandler()
{
	return s_NaNHandler;
}

void setNaNHandler( bool use )
{
	s_NaNHandler = use;
}

/*! \brief Function for sanitizing a buffer of infs/nans - returns true if those are found */
bool sanitize( sampleFrame * src, int frames )
{
	if( !useNaNHandler() )
	{
		return false;
	}

	bool found = false;
	for( int f = 0; f < frames; ++f )
	{
		for( int c = 0; c < 2; ++c )
		{
			if( isinf( src[f][c] ) || isnan( src[f][c] ) )
			{
				#ifdef LMMS_DEBUG
					printf("Bad data, clearing buffer. frame: ");
					printf("%d: value %f\n", f, src[f][c]);
				#endif
				for( int f = 0; f < frames; ++f )
				{
					for( int c = 0; c < 2; ++c )
					{
						src[f][c] = 0.0f;
					}
				}
				found = true;
				return found;
			}
			else
			{
				src[f][c] = qBound( -1000.0f, src[f][c], 1000.0f );
			}
		}
	}
	return found;
}

struct AddOp
{
	void operator()( sampleFrame& dst, const sampleFrame& src ) const
	{
		dst[0] += src[0];
		dst[1] += src[1];
	}
} ;

void add( sampleFrame* dst, const sampleFrame* src, int frames )
{
	run<>( dst, src, frames, AddOp() );
}

struct AddMultipliedOp
{
	AddMultipliedOp( float coeff ) : m_coeff( coeff ) { }

	void operator()( sampleFrame& dst, const sampleFrame& src ) const
	{
		dst[0] += src[0] * m_coeff;
		dst[1] += src[1] * m_coeff;
	}

	const float m_coeff;
} ;

void addMultiplied( sampleFrame* dst, const sampleFrame* src, float coeffSrc, int frames )
{
	run<>( dst, src, frames, AddMultipliedOp(coeffSrc) );
}

struct AddSwappedMultipliedOp
{
	AddSwappedMultipliedOp( float coeff ) : m_coeff( coeff ) { }

	void operator()( sampleFrame& dst, const sampleFrame& src ) const
	{
		dst[0] += src[1] * m_coeff;
		dst[1] += src[0] * m_coeff;
	}

	const float m_coeff;
};

void addSwappedMultiplied( sampleFrame* dst, const sampleFrame* src, float coeffSrc, int frames )
{
	run<>( dst, src, frames, AddSwappedMultipliedOp(coeffSrc) );
}

void addMultipliedByBuffer( sampleFrame* dst, const sampleFrame* src, float coeffSrc, ValueBuffer * coeffSrcBuf, int frames )
{
	for( int f = 0; f < frames; ++f )
	{
		dst[f][0] += src[f][0] * coeffSrc * coeffSrcBuf->values()[f];
		dst[f][1] += src[f][1] * coeffSrc * coeffSrcBuf->values()[f];
	}
}

void addMultipliedByBuffers( sampleFrame* dst, const sampleFrame* src, ValueBuffer * coeffSrcBuf1, ValueBuffer * coeffSrcBuf2, int frames )
{
	for( int f = 0; f < frames; ++f )
	{
		dst[f][0] += src[f][0] * coeffSrcBuf1->values()[f] * coeffSrcBuf2->values()[f];
		dst[f][1] += src[f][1] * coeffSrcBuf1->values()[f] * coeffSrcBuf2->values()[f];
	}

}

void addSanitizedMultipliedByBuffer( sampleFrame* dst, const sampleFrame* src, float coeffSrc, ValueBuffer * coeffSrcBuf, int frames )
{
	if ( !useNaNHandler() )
	{
		addMultipliedByBuffer( dst, src, coeffSrc, coeffSrcBuf,
								frames );
		return;
	}

	for( int f = 0; f < frames; ++f )
	{
		dst[f][0] += ( isinf( src[f][0] ) || isnan( src[f][0] ) ) ? 0.0f : src[f][0] * coeffSrc * coeffSrcBuf->values()[f];
		dst[f][1] += ( isinf( src[f][1] ) || isnan( src[f][1] ) ) ? 0.0f : src[f][1] * coeffSrc * coeffSrcBuf->values()[f];
	}
}

void addSanitizedMultipliedByBuffers( sampleFrame* dst, const sampleFrame* src, ValueBuffer * coeffSrcBuf1, ValueBuffer * coeffSrcBuf2, int frames )
{
	if ( !useNaNHandler() )
	{
		addMultipliedByBuffers( dst, src, coeffSrcBuf1, coeffSrcBuf2,
								frames );
		return;
	}

	for( int f = 0; f < frames; ++f )
	{
		dst[f][0] += ( isinf( src[f][0] ) || isnan( src[f][0] ) )
			? 0.0f
			: src[f][0] * coeffSrcBuf1->values()[f] * coeffSrcBuf2->values()[f];
		dst[f][1] += ( isinf( src[f][1] ) || isnan( src[f][1] ) )
			? 0.0f
			: src[f][1] * coeffSrcBuf1->values()[f] * coeffSrcBuf2->values()[f];
	}

}

struct AddSanitizedMultipliedOp
{
	AddSanitizedMultipliedOp( float coeff ) : m_coeff( coeff ) { }

	void operator()( sampleFrame& dst, const sampleFrame& src ) const
	{
		dst[0] += ( isinf( src[0] ) || isnan( src[0] ) ) ? 0.0f : src[0] * m_coeff;
		dst[1] += ( isinf( src[1] ) || isnan( src[1] ) ) ? 0.0f : src[1] * m_coeff;
	}

	const float m_coeff;
};

void addSanitizedMultiplied( sampleFrame* dst, const sampleFrame* src, float coeffSrc, int frames )
{
	if ( !useNaNHandler() )
	{
		addMultiplied( dst, src, coeffSrc, frames );
		return;
	}

	run<>( dst, src, frames, AddSanitizedMultipliedOp(coeffSrc) );
}

struct AddMultipliedStereoOp
{
	AddMultipliedStereoOp( float coeffLeft, float coeffRight )
	{
		m_coeffs[0] = coeffLeft;
		m_coeffs[1] = coeffRight;
	}

	void operator()( sampleFrame& dst, const sampleFrame& src ) const
	{
		dst[0] += src[0] * m_coeffs[0];
		dst[1] += src[1] * m_coeffs[1];
	}

	float m_coeffs[2];
} ;

void addMultipliedStereo( sampleFrame* dst, const sampleFrame* src, float coeffSrcLeft, float coeffSrcRight, int frames )
{

	run<>( dst, src, frames, AddMultipliedStereoOp(coeffSrcLeft, coeffSrcRight) );
}

struct MultiplyAndAddMultipliedOp
{
	MultiplyAndAddMultipliedOp( float coeffDst, float coeffSrc )
	{
		m_coeffs[0] = coeffDst;
		m_coeffs[1] = coeffSrc;
	}

	void operator()( sampleFrame& dst, const sampleFrame& src ) const
	{
		dst[0] = dst[0]*m_coeffs[0] + src[0]*m_coeffs[1];
		dst[1] = dst[1]*m_coeffs[0] + src[1]*m_coeffs[1];
	}

	float m_coeffs[2];
} ;

void multiplyAndAddMultiplied( sampleFrame* dst, const sampleFrame* src, float coeffDst, float coeffSrc, int frames )
{
	run<>( dst, src, frames, MultiplyAndAddMultipliedOp(coeffDst, coeffSrc) );
}

void multiplyAndAddMultipliedJoined( sampleFrame* dst,
										const sample_t* srcLeft,
										const sample_t* srcRight,
										float coeffDst, float coeffSrc, int frames )
{
	run<>( dst, srcLeft, srcRight, frames, MultiplyAndAddMultipliedOp(coeffDst, coeffSrc) );
}

}


code:
/*
 * Mixer.cpp - audio-device-independent mixer for LMMS
 *
 * 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 "Mixer.h"

#include "denormals.h"

#include "lmmsconfig.h"

#include "AudioPort.h"
#include "FxMixer.h"
#include "MixerWorkerThread.h"
#include "Song.h"
#include "EnvelopeAndLfoParameters.h"
#include "NotePlayHandle.h"
#include "ConfigManager.h"
#include "SamplePlayHandle.h"
#include "MemoryHelper.h"

// platform-specific audio-interface-classes
#include "AudioAlsa.h"
#include "AudioJack.h"
#include "AudioOss.h"
#include "AudioSndio.h"
#include "AudioPortAudio.h"
#include "AudioSoundIo.h"
#include "AudioPulseAudio.h"
#include "AudioSdl.h"
#include "AudioDummy.h"

// platform-specific midi-interface-classes
#include "MidiAlsaRaw.h"
#include "MidiAlsaSeq.h"
#include "MidiJack.h"
#include "MidiOss.h"
#include "MidiSndio.h"
#include "MidiWinMM.h"
#include "MidiApple.h"
#include "MidiDummy.h"

#include "BufferManager.h"

typedef LocklessList<PlayHandle *>::Element LocklessListElement;

static thread_local bool s_renderingThread;

Mixer::Mixer( bool renderOnly ) :
	m_renderOnly( renderOnly ),
	m_framesPerPeriod( DEFAULT_BUFFER_SIZE ),
	m_inputBufferRead( 0 ),
	m_inputBufferWrite( 1 ),
	m_outputBufferRead(nullptr),
	m_outputBufferWrite(nullptr),
	m_workers(),
	m_numWorkers( QThread::idealThreadCount()-1 ),
	m_newPlayHandles( PlayHandle::MaxNumber ),
	m_qualitySettings( qualitySettings::Mode_Draft ),
	m_masterGain( 1.0f ),
	m_isProcessing( false ),
	m_audioDev( NULL ),
	m_oldAudioDev( NULL ),
	m_audioDevStartFailed( false ),
	m_profiler(),
	m_metronomeActive(false),
	m_clearSignal( false ),
	m_changesSignal( false ),
	m_changes( 0 ),
	m_doChangesMutex( QMutex::Recursive ),
	m_waitingForWrite( false )
{
	for( int i = 0; i < 2; ++i )
	{
		m_inputBufferFrames[i] = 0;
		m_inputBufferSize[i] = DEFAULT_BUFFER_SIZE * 100;
		m_inputBuffer[i] = new sampleFrame[ DEFAULT_BUFFER_SIZE * 100 ];
		BufferManager::clear( m_inputBuffer[i], m_inputBufferSize[i] );
	}

	// determine FIFO size and number of frames per period
	int fifoSize = 1;

	// if not only rendering (that is, using the GUI), load the buffer
	// size from user configuration
	if( renderOnly == false )
	{
		m_framesPerPeriod =
			( fpp_t ) ConfigManager::inst()->
				value( "mixer", "framesperaudiobuffer" ).toInt();

		// if the value read from user configuration is not set or
		// lower than the minimum allowed, use the default value and
		// save it to the configuration
		if( m_framesPerPeriod < MINIMUM_BUFFER_SIZE )
		{
			ConfigManager::inst()->setValue( "mixer",
						"framesperaudiobuffer",
						QString::number( DEFAULT_BUFFER_SIZE ) );

			m_framesPerPeriod = DEFAULT_BUFFER_SIZE;
		}
		else if( m_framesPerPeriod > DEFAULT_BUFFER_SIZE )
		{
			fifoSize = m_framesPerPeriod / DEFAULT_BUFFER_SIZE;
			m_framesPerPeriod = DEFAULT_BUFFER_SIZE;
		}
	}

	// allocte the FIFO from the determined size
	m_fifo = new Fifo( fifoSize );

	// now that framesPerPeriod is fixed initialize global BufferManager
	BufferManager::init( m_framesPerPeriod );

	int outputBufferSize = m_framesPerPeriod * sizeof(surroundSampleFrame);
	m_outputBufferRead = static_cast<surroundSampleFrame *>(MemoryHelper::alignedMalloc(outputBufferSize));
	m_outputBufferWrite = static_cast<surroundSampleFrame *>(MemoryHelper::alignedMalloc(outputBufferSize));

	BufferManager::clear(m_outputBufferRead, m_framesPerPeriod);
	BufferManager::clear(m_outputBufferWrite, m_framesPerPeriod);

	for( int i = 0; i < m_numWorkers+1; ++i )
	{
		MixerWorkerThread * wt = new MixerWorkerThread( this );
		if( i < m_numWorkers )
		{
			wt->start( QThread::TimeCriticalPriority );
		}
		m_workers.push_back( wt );
	}
}

Mixer::~Mixer()
{
	runChangesInModel();

	for( int w = 0; w < m_numWorkers; ++w )
	{
		m_workers[w]->quit();
	}

	MixerWorkerThread::startAndWaitForJobs();

	for( int w = 0; w < m_numWorkers; ++w )
	{
		m_workers[w]->wait( 500 );
	}

	while( m_fifo->available() )
	{
		delete[] m_fifo->read();
	}
	delete m_fifo;

	delete m_midiClient;
	delete m_audioDev;

	MemoryHelper::alignedFree(m_outputBufferRead);
	MemoryHelper::alignedFree(m_outputBufferWrite);

	for( int i = 0; i < 2; ++i )
	{
		delete[] m_inputBuffer[i];
	}
}

void Mixer::initDevices()
{
	bool success_ful = false;
	if( m_renderOnly ) {
		m_audioDev = new AudioDummy( success_ful, this );
		m_audioDevName = AudioDummy::name();
		m_midiClient = new MidiDummy;
		m_midiClientName = MidiDummy::name();
	} else {
		m_audioDev = tryAudioDevices();
		m_midiClient = tryMidiClients();
	}
}

void Mixer::startProcessing(bool needsFifo)
{
	if (needsFifo)
	{
		m_fifoWriter = new fifoWriter( this, m_fifo );
		m_fifoWriter->start( QThread::HighPriority );
	}
	else
	{
		m_fifoWriter = NULL;
	}

	m_audioDev->startProcessing();

	m_isProcessing = true;
}

void Mixer::stopProcessing()
{
	m_isProcessing = false;

	if( m_fifoWriter != NULL )
	{
		m_fifoWriter->finish();
		m_fifoWriter->wait();
		m_audioDev->stopProcessing();
		delete m_fifoWriter;
		m_fifoWriter = NULL;
	}
	else
	{
		m_audioDev->stopProcessing();
	}
}

sample_rate_t Mixer::baseSampleRate() const
{
	sample_rate_t sr =
		ConfigManager::inst()->value( "mixer", "samplerate" ).toInt();
	if( sr < 44100 )
	{
		sr = 44100;
	}
	return sr;
}

sample_rate_t Mixer::outputSampleRate() const
{
	return m_audioDev != NULL ? m_audioDev->sampleRate() :
							baseSampleRate();
}

sample_rate_t Mixer::inputSampleRate() const
{
	return m_audioDev != NULL ? m_audioDev->sampleRate() :
							baseSampleRate();
}

sample_rate_t Mixer::processingSampleRate() const
{
	return outputSampleRate() * m_qualitySettings.sampleRateMultiplier();
}

bool Mixer::criticalXRuns() const
{
	return cpuLoad() >= 99 && Engine::getSong()->isExporting() == false;
}

void Mixer::pushInputFrames( sampleFrame * _ab, const f_cnt_t _frames )
{
	requestChangeInModel();

	f_cnt_t frames = m_inputBufferFrames[ m_inputBufferWrite ];
	int size = m_inputBufferSize[ m_inputBufferWrite ];
	sampleFrame * buf = m_inputBuffer[ m_inputBufferWrite ];

	if( frames + _frames > size )
	{
		size = qMax( size * 2, frames + _frames );
		sampleFrame * ab = new sampleFrame[ size ];
		memcpy( ab, buf, frames * sizeof( sampleFrame ) );
		delete [] buf;

		m_inputBufferSize[ m_inputBufferWrite ] = size;
		m_inputBuffer[ m_inputBufferWrite ] = ab;

		buf = ab;
	}

	memcpy( &buf[ frames ], _ab, _frames * sizeof( sampleFrame ) );
	m_inputBufferFrames[ m_inputBufferWrite ] += _frames;

	doneChangeInModel();
}

const surroundSampleFrame * Mixer::renderNextBuffer()
{
	m_profiler.startPeriod();

	s_renderingThread = true;

	if( m_clearSignal )
	{
		m_clearSignal = false;
		clearInternal();
	}

	// remove all play-handles that have to be deleted and delete
	// them if they still exist...
	// maybe this algorithm could be optimized...
	ConstPlayHandleList::Iterator it_rem = m_playHandlesToRemove.begin();
	while( it_rem != m_playHandlesToRemove.end() )
	{
		PlayHandleList::Iterator it = std::find( m_playHandles.begin(), m_playHandles.end(), *it_rem );

		if( it != m_playHandles.end() )
		{
			( *it )->audioPort()->removePlayHandle( ( *it ) );
			if( ( *it )->type() == PlayHandle::TypeNotePlayHandle )
			{
				NotePlayHandleManager::release( (NotePlayHandle*) *it );
			}
			else delete *it;
			m_playHandles.erase( it );
		}

		it_rem = m_playHandlesToRemove.erase( it_rem );
	}

	swapBuffers();

	// prepare master mix (clear internal buffers etc.)
	FxMixer * fxMixer = Engine::fxMixer();
	fxMixer->prepareMasterMix();

	handleMetronome();

	// create play-handles for new notes, samples etc.
	Engine::getSong()->processNextBuffer();

	// add all play-handles that have to be added
	for( LocklessListElement * e = m_newPlayHandles.popList(); e; )
	{
		m_playHandles += e->value;
		LocklessListElement * next = e->next;
		m_newPlayHandles.free( e );
		e = next;
	}

	// STAGE 1: run and render all play handles
	MixerWorkerThread::fillJobQueue<PlayHandleList>( m_playHandles );
	MixerWorkerThread::startAndWaitForJobs();

	// removed all play handles which are done
	for( PlayHandleList::Iterator it = m_playHandles.begin();
						it != m_playHandles.end(); )
	{
		if( ( *it )->affinityMatters() &&
			( *it )->affinity() != QThread::currentThread() )
		{
			++it;
			continue;
		}
		if( ( *it )->isFinished() )
		{
			( *it )->audioPort()->removePlayHandle( ( *it ) );
			if( ( *it )->type() == PlayHandle::TypeNotePlayHandle )
			{
				NotePlayHandleManager::release( (NotePlayHandle*) *it );
			}
			else delete *it;
			it = m_playHandles.erase( it );
		}
		else
		{
			++it;
		}
	}

	// STAGE 2: process effects of all instrument- and sampletracks
	MixerWorkerThread::fillJobQueue<QVector<AudioPort *> >( m_audioPorts );
	MixerWorkerThread::startAndWaitForJobs();

	// STAGE 3: do master mix in FX mixer
	fxMixer->masterMix(m_outputBufferWrite);

	emit nextAudioBuffer(m_outputBufferRead);

	runChangesInModel();

	// and trigger LFOs
	EnvelopeAndLfoParameters::instances()->trigger();
	Controller::triggerFrameCounter();
	AutomatableModel::incrementPeriodCounter();

	s_renderingThread = false;

	m_profiler.finishPeriod( processingSampleRate(), m_framesPerPeriod );

	return m_outputBufferRead;
}

void Mixer::swapBuffers()
{
	m_inputBufferWrite = (m_inputBufferWrite + 1) % 2;
	m_inputBufferRead = (m_inputBufferRead + 1) % 2;
	m_inputBufferFrames[m_inputBufferWrite] = 0;

	std::swap(m_outputBufferRead, m_outputBufferWrite);
	BufferManager::clear(m_outputBufferWrite, m_framesPerPeriod);
}

void Mixer::handleMetronome()
{
	static tick_t lastMetroTicks = -1;

	Song * song = Engine::getSong();
	Song::PlayModes currentPlayMode = song->playMode();

	bool metronomeSupported =
		currentPlayMode == Song::Mode_PlayPattern
		|| currentPlayMode == Song::Mode_PlaySong
		|| currentPlayMode == Song::Mode_PlayBB;

	if (!metronomeSupported || !m_metronomeActive || song->isExporting())
	{
		return;
	}

	// stop crash with metronome if empty project
	if (song->countTracks() == 0)
	{
		return;
	}

	tick_t ticks = song->getPlayPos(currentPlayMode).getTicks();
	tick_t ticksPerBar = TimePos::ticksPerBar();
	int numerator = song->getTimeSigModel().getNumerator();

	if (ticks == lastMetroTicks)
	{
		return;
	}

	if (ticks % (ticksPerBar / 1) == 0)
	{
		addPlayHandle(new SamplePlayHandle("misc/metronome02.ogg"));
	}
	else if (ticks % (ticksPerBar / numerator) == 0)
	{
		addPlayHandle(new SamplePlayHandle("misc/metronome01.ogg"));
	}

	lastMetroTicks = ticks;
}

void Mixer::clear()
{
	m_clearSignal = true;
}

void Mixer::clearNewPlayHandles()
{
	requestChangeInModel();
	for( LocklessListElement * e = m_newPlayHandles.popList(); e; )
	{
		LocklessListElement * next = e->next;
		m_newPlayHandles.free( e );
		e = next;
	}
	doneChangeInModel();
}

// removes all play-handles. this is necessary, when the song is stopped ->
// all remaining notes etc. would be played until their end
void Mixer::clearInternal()
{
	// TODO: m_midiClient->noteOffAll();
	for (auto ph : m_playHandles)
	{
		if (ph->type() != PlayHandle::TypeInstrumentPlayHandle)
		{
			m_playHandlesToRemove.push_back(ph);
		}

	}
}

Mixer::StereoSample Mixer::getPeakValues(sampleFrame * ab, const f_cnt_t frames) const
{
	sample_t peakLeft = 0.0f;
	sample_t peakRight = 0.0f;

	for (f_cnt_t f = 0; f < frames; ++f)
	{
		float const absLeft = qAbs(ab[f][0]);
		float const absRight = qAbs(ab[f][1]);
		if (absLeft > peakLeft)
		{
			peakLeft = absLeft;
		}

		if (absRight > peakRight)
		{
			peakRight = absRight;
		}
	}

	return StereoSample(peakLeft, peakRight);
}

void Mixer::changeQuality(const struct qualitySettings & qs)
{
	// don't delete the audio-device
	stopProcessing();

	m_qualitySettings = qs;
	m_audioDev->applyQualitySettings();

	emit sampleRateChanged();
	emit qualitySettingsChanged();

	startProcessing();
}

void Mixer::doSetAudioDevice( AudioDevice * _dev )
{
	// TODO: Use shared_ptr here in the future.
	// Currently, this is safe, because this is only called by
	// ProjectRenderer, and after ProjectRenderer calls this function,
	// it does not access the old device anymore.
	if( m_audioDev != m_oldAudioDev ) {delete m_audioDev;}

	if( _dev )
	{
		m_audioDev = _dev;
	}
	else
	{
		printf( "param _dev == NULL in Mixer::setAudioDevice(...). "
					"Trying any working audio-device\n" );
		m_audioDev = tryAudioDevices();
	}
}

void Mixer::setAudioDevice(AudioDevice * _dev,
				const struct qualitySettings & _qs,
				bool _needs_fifo,
				bool startNow)
{
	stopProcessing();

	m_qualitySettings = _qs;

	doSetAudioDevice( _dev );

	emit qualitySettingsChanged();
	emit sampleRateChanged();

	if (startNow) {startProcessing( _needs_fifo );}
}

void Mixer::storeAudioDevice()
{
	if( !m_oldAudioDev )
	{
		m_oldAudioDev = m_audioDev;
	}
}

void Mixer::restoreAudioDevice()
{
	if( m_oldAudioDev && m_audioDev != m_oldAudioDev )
	{
		stopProcessing();
		delete m_audioDev;

		m_audioDev = m_oldAudioDev;
		emit sampleRateChanged();

		startProcessing();
	}
	m_oldAudioDev = NULL;
}

void Mixer::removeAudioPort(AudioPort * port)
{
	requestChangeInModel();

	QVector<AudioPort *>::Iterator it = std::find(m_audioPorts.begin(), m_audioPorts.end(), port);
	if (it != m_audioPorts.end())
	{
		m_audioPorts.erase(it);
	}
	doneChangeInModel();
}

bool Mixer::addPlayHandle( PlayHandle* handle )
{
	if( criticalXRuns() == false )
	{
		m_newPlayHandles.push( handle );
		handle->audioPort()->addPlayHandle( handle );
		return true;
	}

	if( handle->type() == PlayHandle::TypeNotePlayHandle )
	{
		NotePlayHandleManager::release( (NotePlayHandle*)handle );
	}
	else delete handle;

	return false;
}

void Mixer::removePlayHandle(PlayHandle * ph)
{
	requestChangeInModel();
	// check thread affinity as we must not delete play-handles
	// which were created in a thread different than mixer thread
	if (ph->affinityMatters() && ph->affinity() == QThread::currentThread())
	{
		ph->audioPort()->removePlayHandle(ph);
		bool removedFromList = false;
		// Check m_newPlayHandles first because doing it the other way around
		// creates a race condition
		for( LocklessListElement * e = m_newPlayHandles.first(),
				* ePrev = NULL; e; ePrev = e, e = e->next )
		{
			if (e->value == ph)
			{
				if( ePrev )
				{
					ePrev->next = e->next;
				}
				else
				{
					m_newPlayHandles.setFirst( e->next );
				}
				m_newPlayHandles.free( e );
				removedFromList = true;
				break;
			}
		}
		// Now check m_playHandles
		PlayHandleList::Iterator it = std::find(m_playHandles.begin(), m_playHandles.end(), ph);
		if (it != m_playHandles.end())
		{
			m_playHandles.erase(it);
			removedFromList = true;
		}
		// Only deleting PlayHandles that were actually found in the list
		// "fixes crash when previewing a preset under high load"
		// (See tobydox's 2008 commit 4583e48)
		if ( removedFromList )
		{
			if (ph->type() == PlayHandle::TypeNotePlayHandle)
			{
				NotePlayHandleManager::release(dynamic_cast<NotePlayHandle*>(ph));
			}
			else { delete ph; }
		}
	}
	else
	{
		m_playHandlesToRemove.push_back(ph);
	}
	doneChangeInModel();
}

void Mixer::removePlayHandlesOfTypes(Track * track, const quint8 types)
{
	requestChangeInModel();
	PlayHandleList::Iterator it = m_playHandles.begin();
	while( it != m_playHandles.end() )
	{
		if ((*it)->isFromTrack(track) && ((*it)->type() & types))
		{
			( *it )->audioPort()->removePlayHandle( ( *it ) );
			if( ( *it )->type() == PlayHandle::TypeNotePlayHandle )
			{
				NotePlayHandleManager::release( (NotePlayHandle*) *it );
			}
			else delete *it;
			it = m_playHandles.erase( it );
		}
		else
		{
			++it;
		}
	}
	doneChangeInModel();
}

void Mixer::requestChangeInModel()
{
	if( s_renderingThread )
		return;

	m_changesMutex.lock();
	m_changes++;
	m_changesMutex.unlock();

	m_doChangesMutex.lock();
	m_waitChangesMutex.lock();
	if (m_isProcessing && !m_waitingForWrite && !m_changesSignal)
	{
		m_changesSignal = true;
		m_changesRequestCondition.wait( &m_waitChangesMutex );
	}
	m_waitChangesMutex.unlock();
}

void Mixer::doneChangeInModel()
{
	if( s_renderingThread )
		return;

	m_changesMutex.lock();
	bool moreChanges = --m_changes;
	m_changesMutex.unlock();

	if( !moreChanges )
	{
		m_changesSignal = false;
		m_changesMixerCondition.wakeOne();
	}
	m_doChangesMutex.unlock();
}

void Mixer::runChangesInModel()
{
	if( m_changesSignal )
	{
		m_waitChangesMutex.lock();
		// allow changes in the model from other threads ...
		m_changesRequestCondition.wakeOne();
		// ... and wait until they are done
		m_changesMixerCondition.wait( &m_waitChangesMutex );
		m_waitChangesMutex.unlock();
	}
}

bool Mixer::isAudioDevNameValid(QString name)
{
#ifdef LMMS_HAVE_SDL
	if (name == AudioSdl::name())
	{
		return true;
	}
#endif

#ifdef LMMS_HAVE_ALSA
	if (name == AudioAlsa::name())
	{
		return true;
	}
#endif

#ifdef LMMS_HAVE_PULSEAUDIO
	if (name == AudioPulseAudio::name())
	{
		return true;
	}
#endif

#ifdef LMMS_HAVE_OSS
	if (name == AudioOss::name())
	{
		return true;
	}
#endif

#ifdef LMMS_HAVE_SNDIO
	if (name == AudioSndio::name())
	{
		return true;
	}
#endif

#ifdef LMMS_HAVE_JACK
	if (name == AudioJack::name())
	{
		return true;
	}
#endif

#ifdef LMMS_HAVE_PORTAUDIO
	if (name == AudioPortAudio::name())
	{
		return true;
	}
#endif

#ifdef LMMS_HAVE_SOUNDIO
	if (name == AudioSoundIo::name())
	{
		return true;
	}
#endif

	if (name == AudioDummy::name())
	{
		return true;
	}

	return false;
}

bool Mixer::isMidiDevNameValid(QString name)
{
#ifdef LMMS_HAVE_ALSA
	if (name == MidiAlsaSeq::name() || name == MidiAlsaRaw::name())
	{
		return true;
	}
#endif

#ifdef LMMS_HAVE_JACK
	if (name == MidiJack::name())
	{
		return true;
	}
#endif

#ifdef LMMS_HAVE_OSS
	if (name == MidiOss::name())
	{
		return true;
	}
#endif

#ifdef LMMS_HAVE_SNDIO
	if (name == MidiSndio::name())
	{
		return true;
	}
#endif

#ifdef LMMS_BUILD_WIN32
	if (name == MidiWinMM::name())
	{
		return true;
	}
#endif

#ifdef LMMS_BUILD_APPLE
    if (name == MidiApple::name())
    {
		return true;
    }
#endif

    if (name == MidiDummy::name())
    {
		return true;
    }

	return false;
}

AudioDevice * Mixer::tryAudioDevices()
{
	bool success_ful = false;
	AudioDevice * dev = NULL;
	QString dev_name = ConfigManager::inst()->value( "mixer", "audiodev" );
	if( !isAudioDevNameValid( dev_name ) )
	{
		dev_name = "";
	}

	m_audioDevStartFailed = false;

#ifdef LMMS_HAVE_SDL
	if( dev_name == AudioSdl::name() || dev_name == "" )
	{
		dev = new AudioSdl( success_ful, this );
		if( success_ful )
		{
			m_audioDevName = AudioSdl::name();
			return dev;
		}
		delete dev;
	}
#endif

#ifdef LMMS_HAVE_ALSA
	if( dev_name == AudioAlsa::name() || dev_name == "" )
	{
		dev = new AudioAlsa( success_ful, this );
		if( success_ful )
		{
			m_audioDevName = AudioAlsa::name();
			return dev;
		}
		delete dev;
	}
#endif

#ifdef LMMS_HAVE_PULSEAUDIO
	if( dev_name == AudioPulseAudio::name() || dev_name == "" )
	{
		dev = new AudioPulseAudio( success_ful, this );
		if( success_ful )
		{
			m_audioDevName = AudioPulseAudio::name();
			return dev;
		}
		delete dev;
	}
#endif

#ifdef LMMS_HAVE_OSS
	if( dev_name == AudioOss::name() || dev_name == "" )
	{
		dev = new AudioOss( success_ful, this );
		if( success_ful )
		{
			m_audioDevName = AudioOss::name();
			return dev;
		}
		delete dev;
	}
#endif

#ifdef LMMS_HAVE_SNDIO
	if( dev_name == AudioSndio::name() || dev_name == "" )
	{
		dev = new AudioSndio( success_ful, this );
		if( success_ful )
		{
			m_audioDevName = AudioSndio::name();
			return dev;
		}
		delete dev;
	}
#endif

#ifdef LMMS_HAVE_JACK
	if( dev_name == AudioJack::name() || dev_name == "" )
	{
		dev = new AudioJack( success_ful, this );
		if( success_ful )
		{
			m_audioDevName = AudioJack::name();
			return dev;
		}
		delete dev;
	}
#endif

#ifdef LMMS_HAVE_PORTAUDIO
	if( dev_name == AudioPortAudio::name() || dev_name == "" )
	{
		dev = new AudioPortAudio( success_ful, this );
		if( success_ful )
		{
			m_audioDevName = AudioPortAudio::name();
			return dev;
		}
		delete dev;
	}
#endif

#ifdef LMMS_HAVE_SOUNDIO
	if( dev_name == AudioSoundIo::name() || dev_name == "" )
	{
		dev = new AudioSoundIo( success_ful, this );
		if( success_ful )
		{
			m_audioDevName = AudioSoundIo::name();
			return dev;
		}
		delete dev;
	}
#endif

	// add more device-classes here...
	//dev = new audioXXXX( SAMPLE_RATES[m_qualityLevel], success_ful, this );
	//if( sucess_ful )
	//{
	//	return dev;
	//}
	//delete dev

	if( dev_name != AudioDummy::name() )
	{
		printf( "No audio-driver working - falling back to dummy-audio-"
			"driver\nYou can render your songs and listen to the output "
			"files...\n" );

		m_audioDevStartFailed = true;
	}

	m_audioDevName = AudioDummy::name();

	return new AudioDummy( success_ful, this );
}

MidiClient * Mixer::tryMidiClients()
{
	QString client_name = ConfigManager::inst()->value( "mixer",
								"mididev" );
	if( !isMidiDevNameValid( client_name ) )
	{
		client_name = "";
	}

#ifdef LMMS_HAVE_ALSA
	if( client_name == MidiAlsaSeq::name() || client_name == "" )
	{
		MidiAlsaSeq * malsas = new MidiAlsaSeq;
		if( malsas->isRunning() )
		{
			m_midiClientName = MidiAlsaSeq::name();
			return malsas;
		}
		delete malsas;
	}

	if( client_name == MidiAlsaRaw::name() || client_name == "" )
	{
		MidiAlsaRaw * malsar = new MidiAlsaRaw;
		if( malsar->isRunning() )
		{
			m_midiClientName = MidiAlsaRaw::name();
			return malsar;
		}
		delete malsar;
	}
#endif

#ifdef LMMS_HAVE_JACK
	if( client_name == MidiJack::name() || client_name == "" )
	{
		MidiJack * mjack = new MidiJack;
		if( mjack->isRunning() )
		{
			m_midiClientName = MidiJack::name();
			return mjack;
		}
		delete mjack;
	}
#endif

#ifdef LMMS_HAVE_OSS
	if( client_name == MidiOss::name() || client_name == "" )
	{
		MidiOss * moss = new MidiOss;
		if( moss->isRunning() )
		{
			m_midiClientName = MidiOss::name();
			return moss;
		}
		delete moss;
	}
#endif

#ifdef LMMS_HAVE_SNDIO
	if( client_name == MidiSndio::name() || client_name == "" )
	{
		MidiSndio * msndio = new MidiSndio;
		if( msndio->isRunning() )
		{
			m_midiClientName = MidiSndio::name();
			return msndio;
		}
		delete msndio;
	}
#endif

#ifdef LMMS_BUILD_WIN32
	if( client_name == MidiWinMM::name() || client_name == "" )
	{
		MidiWinMM * mwmm = new MidiWinMM;
//		if( moss->isRunning() )
		{
			m_midiClientName = MidiWinMM::name();
			return mwmm;
		}
		delete mwmm;
	}
#endif

#ifdef LMMS_BUILD_APPLE
    printf( "trying midi apple...\n" );
    if( client_name == MidiApple::name() || client_name == "" )
    {
        MidiApple * mapple = new MidiApple;
        m_midiClientName = MidiApple::name();
        printf( "Returning midi apple\n" );
        return mapple;
    }
    printf( "midi apple didn't work: client_name=%s\n", client_name.toUtf8().constData());
#endif

	if(client_name != MidiDummy::name())
	{
		if (client_name.isEmpty())
		{
			printf("Unknown MIDI-client. ");
		}
		else
		{
			printf("Couldn't create %s MIDI-client. ", client_name.toUtf8().constData());
		}
		printf("Will use dummy-MIDI-client.\n");
	}

	m_midiClientName = MidiDummy::name();

	return new MidiDummy;
}

Mixer::fifoWriter::fifoWriter( Mixer* mixer, Fifo * fifo ) :
	m_mixer( mixer ),
	m_fifo( fifo ),
	m_writing( true )
{
	setObjectName("Mixer::fifoWriter");
}

void Mixer::fifoWriter::finish()
{
	m_writing = false;
}

void Mixer::fifoWriter::run()
{
	disable_denormals();

#if 0
#if defined(LMMS_BUILD_LINUX) || defined(LMMS_BUILD_FREEBSD)
#ifdef LMMS_HAVE_SCHED_H
	cpu_set_t mask;
	CPU_ZERO( &mask );
	CPU_SET( 0, &mask );
	sched_setaffinity( 0, sizeof( mask ), &mask );
#endif
#endif
#endif

	const fpp_t frames = m_mixer->framesPerPeriod();
	while( m_writing )
	{
		surroundSampleFrame * buffer = new surroundSampleFrame[frames];
		const surroundSampleFrame * b = m_mixer->renderNextBuffer();
		memcpy( buffer, b, frames * sizeof( surroundSampleFrame ) );
		write( buffer );
	}

	// Let audio backend stop processing
	write( NULL );
	m_fifo->waitUntilRead();
}

void Mixer::fifoWriter::write( surroundSampleFrame * buffer )
{
	m_mixer->m_waitChangesMutex.lock();
	m_mixer->m_waitingForWrite = true;
	m_mixer->m_waitChangesMutex.unlock();
	m_mixer->runChangesInModel();

	m_fifo->write( buffer );

	m_mixer->m_doChangesMutex.lock();
	m_mixer->m_waitingForWrite = false;
	m_mixer->m_doChangesMutex.unlock();
}


code:
/*
 * MixerProfiler.cpp - class for profiling performance of Mixer
 *
 * Copyright (c) 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 "MixerProfiler.h"

MixerProfiler::MixerProfiler() :
	m_periodTimer(),
	m_cpuLoad( 0 ),
	m_outputFile()
{
}

MixerProfiler::~MixerProfiler()
{
}

void MixerProfiler::finishPeriod( sample_rate_t sampleRate, fpp_t framesPerPeriod )
{
	int periodElapsed = m_periodTimer.elapsed();

	const float newCpuLoad = periodElapsed / 10000.0f * sampleRate / framesPerPeriod;
    m_cpuLoad = qBound<int>( 0, ( newCpuLoad * 0.1f + m_cpuLoad * 0.9f ), 100 );

	if( m_outputFile.isOpen() )
	{
		m_outputFile.write( QString( "%1\n" ).arg( periodElapsed ).toLatin1() );
	}
}

void MixerProfiler::setOutputFile( const QString& outputFile )
{
	m_outputFile.close();
	m_outputFile.setFileName( outputFile );
	m_outputFile.open( QFile::WriteOnly | QFile::Truncate );
}


code:
/*
 * MixerWorkerThread.cpp - implementation of MixerWorkerThread
 *
 * Copyright (c) 2009-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 "MixerWorkerThread.h"

#include <QDebug>
#include <QMutex>
#include <QWaitCondition>

#include "denormals.h"
#include "ThreadableJob.h"
#include "Mixer.h"

#if defined(LMMS_HOST_X86) || defined(LMMS_HOST_X86_64)
#include <xmmintrin.h>
#endif

MixerWorkerThread::JobQueue MixerWorkerThread::globalJobQueue;
QWaitCondition * MixerWorkerThread::queueReadyWaitCond = NULL;
QList<MixerWorkerThread *> MixerWorkerThread::workerThreads;

// implementation of internal JobQueue
void MixerWorkerThread::JobQueue::reset( OperationMode _opMode )
{
	m_writeIndex = 0;
	m_itemsDone = 0;
	m_opMode = _opMode;
}

void MixerWorkerThread::JobQueue::addJob( ThreadableJob * _job )
{
	if( _job->requiresProcessing() )
	{
		// update job state
		_job->queue();
		// actually queue the job via atomic operations
		auto index = m_writeIndex++;
		if (index < JOB_QUEUE_SIZE) {
			m_items[index] = _job;
		} else {
			qWarning() << "Job queue is full!";
			++m_itemsDone;
		}
	}
}

void MixerWorkerThread::JobQueue::run()
{
	bool processedJob = true;
	while (processedJob && m_itemsDone < m_writeIndex)
	{
		processedJob = false;
		for( int i = 0; i < m_writeIndex && i < JOB_QUEUE_SIZE; ++i )
		{
			ThreadableJob * job = m_items[i].exchange(nullptr);
			if( job )
			{
				job->process();
				processedJob = true;
				++m_itemsDone;
			}
		}
		// always exit loop if we're not in dynamic mode
		processedJob = processedJob && ( m_opMode == Dynamic );
	}
}

void MixerWorkerThread::JobQueue::wait()
{
	while (m_itemsDone < m_writeIndex)
	{
#if defined(LMMS_HOST_X86) || defined(LMMS_HOST_X86_64)
		_mm_pause();
#endif
	}
}

// implementation of worker threads

MixerWorkerThread::MixerWorkerThread( Mixer* mixer ) :
	QThread( mixer ),
	m_quit( false )
{
	// initialize global static data
	if( queueReadyWaitCond == NULL )
	{
		queueReadyWaitCond = new QWaitCondition;
	}

	// keep track of all instantiated worker threads - this is used for
	// processing the last worker thread "inline", see comments in
	// MixerWorkerThread::startAndWaitForJobs() for details
	workerThreads << this;

	resetJobQueue();
}

MixerWorkerThread::~MixerWorkerThread()
{
	workerThreads.removeAll( this );
}

void MixerWorkerThread::quit()
{
	m_quit = true;
	resetJobQueue();
}

void MixerWorkerThread::startAndWaitForJobs()
{
	queueReadyWaitCond->wakeAll();
	// The last worker-thread is never started. Instead it's processed "inline"
	// i.e. within the global Mixer thread. This way we can reduce latencies
	// that otherwise would be caused by synchronizing with another thread.
	globalJobQueue.run();
	globalJobQueue.wait();
}

void MixerWorkerThread::run()
{
	MemoryManager::ThreadGuard mmThreadGuard; Q_UNUSED(mmThreadGuard);
	disable_denormals();

	QMutex m;
	while( m_quit == false )
	{
		m.lock();
		queueReadyWaitCond->wait( &m );
		globalJobQueue.run();
		m.unlock();
	}
}


code:
/*
 * Model.cpp - implementation of Model base class
 *
 * Copyright (c) 2007-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 "Model.h"

QString Model::fullDisplayName() const
{
	const QString & n = displayName();
	if( parentModel() )
	{
		const QString p = parentModel()->fullDisplayName();
		if( n.isEmpty() && p.isEmpty() )
		{
			return QString();
		}
		else if( p.isEmpty() )
		{
			return n;
		}
		return p + ">" + n;
	}
	return n;
}


code:
/*
 * ModelVisitor.cpp - visitors for automatable models
 *
 * Copyright (c) 2019-2019 Johannes Lorenz <j.git$$$lorenz-ho.me, $$$=@>
 *
 * 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 "ModelVisitor.h"

#include "AutomatableModel.h"
#include "ComboBoxModel.h"
#include "TempoSyncKnobModel.h"

void ModelVisitor::visit(BoolModel &m) { up(m); }
void ModelVisitor::visit(IntModel &m) { up(m); }
void ModelVisitor::visit(FloatModel &m) { up(m); }
void ModelVisitor::visit(ComboBoxModel &m) { up<IntModel>(m); }
void ModelVisitor::visit(TempoSyncKnobModel &m) { up<FloatModel>(m); }

void ConstModelVisitor::visit(const BoolModel &m) { up(m); }
void ConstModelVisitor::visit(const IntModel &m) { up(m); }
void ConstModelVisitor::visit(const FloatModel &m) { up(m); }
void ConstModelVisitor::visit(const ComboBoxModel &m) { up<IntModel>(m); }
void ConstModelVisitor::visit(const TempoSyncKnobModel &m) { up<FloatModel>(m); }

ModelVisitor::~ModelVisitor() {}
ConstModelVisitor::~ConstModelVisitor() {}


code:
/*
 * Note.cpp - implementation of class note
 *
 * 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 <math.h>

#include "Note.h"
#include "DetuningHelper.h"

Note::Note( const TimePos & length, const TimePos & pos,
		int key, volume_t volume, panning_t panning,
						DetuningHelper * detuning ) :
	m_selected( false ),
	m_oldKey( qBound( 0, key, NumKeys ) ),
	m_oldPos( pos ),
	m_oldLength( length ),
	m_isPlaying( false ),
	m_key( qBound( 0, key, NumKeys ) ),
	m_volume( qBound( MinVolume, volume, MaxVolume ) ),
	m_panning( qBound( PanningLeft, panning, PanningRight ) ),
	m_length( length ),
	m_pos( pos ),
	m_detuning( NULL )
{
	if( detuning )
	{
		m_detuning = sharedObject::ref( detuning );
	}
	else
	{
		createDetuning();
	}
}

Note::Note( const Note & note ) :
	SerializingObject( note ),
	m_selected( note.m_selected ),
	m_oldKey( note.m_oldKey ),
	m_oldPos( note.m_oldPos ),
	m_oldLength( note.m_oldLength ),
	m_isPlaying( note.m_isPlaying ),
	m_key( note.m_key),
	m_volume( note.m_volume ),
	m_panning( note.m_panning ),
	m_length( note.m_length ),
	m_pos( note.m_pos ),
	m_detuning( NULL )
{
	if( note.m_detuning )
	{
		m_detuning = sharedObject::ref( note.m_detuning );
	}
}

Note::~Note()
{
	if( m_detuning )
	{
		sharedObject::unref( m_detuning );
	}
}

void Note::setLength( const TimePos & length )
{
	m_length = length;
}

void Note::setPos( const TimePos & pos )
{
	m_pos = pos;
}

void Note::setKey( const int key )
{
	const int k = qBound( 0, key, NumKeys - 1 );
	m_key = k;
}

void Note::setVolume( volume_t volume )
{
	const volume_t v = qBound( MinVolume, volume, MaxVolume );
	m_volume = v;
}

void Note::setPanning( panning_t panning )
{
	const panning_t p = qBound( PanningLeft, panning, PanningRight );
	m_panning = p;
}

TimePos Note::quantized( const TimePos & m, const int qGrid )
{
	float p = ( (float) m / qGrid );
	if( p - floorf( p ) < 0.5f )
	{
		return static_cast<int>( p ) * qGrid;
	}
	return static_cast<int>( p + 1 ) * qGrid;
}

void Note::quantizeLength( const int qGrid )
{
	setLength( quantized( length(), qGrid ) );
	if( length() == 0 )
	{
		setLength( qGrid );
	}
}

void Note::quantizePos( const int qGrid )
{
	setPos( quantized( pos(), qGrid ) );
}

void Note::saveSettings( QDomDocument & doc, QDomElement & parent )
{
	parent.setAttribute( "key", m_key );
	parent.setAttribute( "vol", m_volume );
	parent.setAttribute( "pan", m_panning );
	parent.setAttribute( "len", m_length );
	parent.setAttribute( "pos", m_pos );

	if( m_detuning && m_length )
	{
		m_detuning->saveSettings( doc, parent );
	}
}

void Note::loadSettings( const QDomElement & _this )
{
	const int oldKey = _this.attribute( "tone" ).toInt() + _this.attribute( "oct" ).toInt() * KeysPerOctave;
	m_key = qMax( oldKey, _this.attribute( "key" ).toInt() );
	m_volume = _this.attribute( "vol" ).toInt();
	m_panning = _this.attribute( "pan" ).toInt();
	m_length = _this.attribute( "len" ).toInt();
	m_pos = _this.attribute( "pos" ).toInt();

	if( _this.hasChildNodes() )
	{
		createDetuning();
		m_detuning->loadSettings( _this );
	}
}

void Note::createDetuning()
{
	if( m_detuning == NULL )
	{
		m_detuning = new DetuningHelper;
		(void) m_detuning->automationPattern();
		m_detuning->setRange( -MaxDetuning, MaxDetuning, 0.5f );
		m_detuning->automationPattern()->setProgressionType( AutomationPattern::LinearProgression );
	}
}

bool Note::hasDetuningInfo() const
{
	return m_detuning && m_detuning->hasAutomation();
}

bool Note::withinRange(int tickStart, int tickEnd) const
{
	return pos().getTicks() >= tickStart && pos().getTicks() <= tickEnd
		&& length().getTicks() != 0;
}