code:
/*
 * ConfigManager.cpp - implementation of class ConfigManager
 *
 * 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 <QDomElement>
#include <QDir>
#include <QMessageBox>
#include <QApplication>
#include <QStandardPaths>
#include <QtCore/QTextStream>

#include "ConfigManager.h"
#include "MainWindow.h"
#include "ProjectVersion.h"
#include "GuiApplication.h"

#include "lmmsversion.h"

// Vector with all the upgrade methods
const std::vector<ConfigManager::UpgradeMethod> ConfigManager::UPGRADE_METHODS = {
	&ConfigManager::upgrade_1_1_90    ,    &ConfigManager::upgrade_1_1_91
};

static inline QString ensureTrailingSlash(const QString & s )
{
	if(! s.isEmpty() && !s.endsWith('/') && !s.endsWith('\\'))
	{
		return s + '/';
	}
	return s;
}

ConfigManager * ConfigManager::s_instanceOfMe = NULL;

ConfigManager::ConfigManager() :
	m_version(defaultVersion()),
	m_configVersion( UPGRADE_METHODS.size() )
{
	if (QFileInfo::exists(qApp->applicationDirPath() + PORTABLE_MODE_FILE))
	{
		initPortableWorkingDir();
	}
	else
	{
		initInstalledWorkingDir();
	}
	m_dataDir = "data:/";
	m_vstDir = m_workingDir + "vst/";
	m_sf2Dir = m_workingDir + SF2_PATH;
	m_gigDir = m_workingDir + GIG_PATH;
	m_themeDir = defaultThemeDir();
	if (!qgetenv("LMMS_DATA_DIR").isEmpty())
	{
		QDir::addSearchPath("data", QString::fromLocal8Bit(qgetenv("LMMS_DATA_DIR")));
	}
	initDevelopmentWorkingDir();

#ifdef LMMS_BUILD_WIN32
	QDir::addSearchPath("data", qApp->applicationDirPath() + "/data/");
#else
	QDir::addSearchPath("data", qApp->applicationDirPath().section('/', 0, -2) + "/share/lmms/");
#endif

}

ConfigManager::~ConfigManager()
{
	saveConfigFile();
}

void ConfigManager::upgrade_1_1_90()
{
	// Remove trailing " (bad latency!)" string which was once saved with PulseAudio
	if(value("mixer", "audiodev").startsWith("PulseAudio ("))
	{
		setValue("mixer", "audiodev", "PulseAudio");
	}

	// MidiAlsaRaw used to store the device info as "Device" instead of "device"
	if (value("MidiAlsaRaw", "device").isNull())
	{
		// copy "device" = "Device" and then delete the old "Device" (further down)
		QString oldDevice = value("MidiAlsaRaw", "Device");
		setValue("MidiAlsaRaw", "device", oldDevice);
	}
	if (!value("MidiAlsaRaw", "device").isNull())
	{
		// delete the old "Device" in the case that we just copied it to "device"
		//   or if the user somehow set both the "Device" and "device" fields
		deleteValue("MidiAlsaRaw", "Device");
	}
}

void ConfigManager::upgrade_1_1_91()
{
	// rename displaydbv to displaydbfs
	if (!value("app", "displaydbv").isNull()) {
		setValue("app", "displaydbfs", value("app", "displaydbv"));
		deleteValue("app", "displaydbv");
	}
}

void ConfigManager::upgrade()
{
	// Skip the upgrade if versions match
	if (m_version == LMMS_VERSION)
	{
		return;
	}

	// Runs all necessary upgrade methods
	std::for_each( UPGRADE_METHODS.begin() + m_configVersion, UPGRADE_METHODS.end(),
		[this](UpgradeMethod um)
		{
			(this->*um)();
		}
	);

	ProjectVersion createdWith = m_version;

	// Don't use old themes as they break the UI (i.e. 0.4 != 1.0, etc)
	if (createdWith.setCompareType(ProjectVersion::Minor) != LMMS_VERSION)
	{
		m_themeDir = defaultThemeDir();
	}

	// Bump the version, now that we are upgraded
	m_version = LMMS_VERSION;
	m_configVersion = UPGRADE_METHODS.size();
}

QString ConfigManager::defaultVersion() const
{
	return LMMS_VERSION;
}

QStringList ConfigManager::availableVstEmbedMethods()
{
	QStringList methods;
	methods.append("none");
#if QT_VERSION >= 0x050100
	methods.append("qt");
#endif
#ifdef LMMS_BUILD_WIN32
	methods.append("win32");
#endif
#ifdef LMMS_BUILD_LINUX
	if (static_cast<QGuiApplication*>(QApplication::instance())->
		platformName() == "xcb")
	{
		methods.append("xembed");
	}
#endif
	return methods;
}

QString ConfigManager::vstEmbedMethod() const
{
	QStringList methods = availableVstEmbedMethods();
	QString defaultMethod = *(methods.end() - 1);
	QString currentMethod = value( "ui", "vstembedmethod", defaultMethod );
	return methods.contains(currentMethod) ? currentMethod : defaultMethod;
}

bool ConfigManager::hasWorkingDir() const
{
	return QDir(m_workingDir).exists();
}

void ConfigManager::setWorkingDir(const QString & workingDir)
{
	m_workingDir = ensureTrailingSlash(QDir::cleanPath(workingDir));
}

void ConfigManager::setVSTDir(const QString & vstDir)
{
	m_vstDir = ensureTrailingSlash(vstDir);
}

void ConfigManager::setLADSPADir(const QString & ladspaDir)
{
	m_ladspaDir = ladspaDir;
}

void ConfigManager::setSTKDir(const QString & stkDir)
{
#ifdef LMMS_HAVE_STK
	m_stkDir = ensureTrailingSlash(stkDir);
#endif
}

void ConfigManager::setSF2Dir(const QString & sf2Dir)
{
	m_sf2Dir = sf2Dir;
}

void ConfigManager::setSF2File(const QString & sf2File)
{
#ifdef LMMS_HAVE_FLUIDSYNTH
	m_sf2File = sf2File;
#endif
}

void ConfigManager::setGIGDir(const QString & gigDir)
{
	m_gigDir = gigDir;
}

void ConfigManager::setThemeDir(const QString & themeDir)
{
	m_themeDir = ensureTrailingSlash(themeDir);
}

void ConfigManager::setBackgroundPicFile(const QString & backgroundPicFile)
{
	m_backgroundPicFile = backgroundPicFile;
}

void ConfigManager::createWorkingDir()
{
	QDir().mkpath(m_workingDir);

	QDir().mkpath(userProjectsDir());
	QDir().mkpath(userTemplateDir());
	QDir().mkpath(userSamplesDir());
	QDir().mkpath(userPresetsDir());
	QDir().mkpath(userGigDir());
	QDir().mkpath(userSf2Dir());
	QDir().mkpath(userVstDir());
	QDir().mkpath(userLadspaDir());
}

void ConfigManager::addRecentlyOpenedProject(const QString & file)
{
	QFileInfo recentFile(file);
	if(recentFile.suffix().toLower() == "mmp" ||
		recentFile.suffix().toLower() == "mmpz" ||
		recentFile.suffix().toLower() == "mpt")
	{
		m_recentlyOpenedProjects.removeAll(file);
		if(m_recentlyOpenedProjects.size() > 50)
		{
			m_recentlyOpenedProjects.removeLast();
		}
		m_recentlyOpenedProjects.push_front(file);
		ConfigManager::inst()->saveConfigFile();
	}
}

const QString & ConfigManager::value(const QString & cls,
					const QString & attribute) const
{
	if(m_settings.contains(cls))
	{
		for(stringPairVector::const_iterator it =
						m_settings[cls].begin();
					it != m_settings[cls].end(); ++it)
		{
			if((*it).first == attribute)
			{
				return (*it).second ;
			}
		}
	}
	static QString empty;
	return empty;
}

const QString & ConfigManager::value(const QString & cls,
				      const QString & attribute,
				      const QString & defaultVal) const
{
	const QString & val = value(cls, attribute);
	return val.isEmpty() ? defaultVal : val;
}

void ConfigManager::setValue(const QString & cls,
				const QString & attribute,
				const QString & value)
{
	if(m_settings.contains(cls))
	{
		for(QPair<QString, QString>& pair : m_settings[cls])
		{
			if(pair.first == attribute)
			{
				if (pair.second != value)
				{
					pair.second = value;
					emit valueChanged(cls, attribute, value);
				}
				return;
			}
		}
	}
	// not in map yet, so we have to add it...
	m_settings[cls].push_back(qMakePair(attribute, value));
}

void ConfigManager::deleteValue(const QString & cls, const QString & attribute)
{
	if(m_settings.contains(cls))
	{
		for(stringPairVector::iterator it = m_settings[cls].begin();
					it != m_settings[cls].end(); ++it)
		{
			if((*it).first == attribute)
			{
				m_settings[cls].erase(it);
				return;
			}
		}
	}
}

void ConfigManager::loadConfigFile(const QString & configFile)
{
	// read the XML file and create DOM tree
	// Allow configuration file override through --config commandline option
	if (!configFile.isEmpty())
	{
		m_lmmsRcFile = configFile;
	}

	QFile cfg_file(m_lmmsRcFile);
	QDomDocument dom_tree;

	if(cfg_file.open(QIODevice::ReadOnly))
	{
		QString errorString;
		int errorLine, errorCol;
		if(dom_tree.setContent(&cfg_file, false, &errorString, &errorLine, &errorCol))
		{
			// get the head information from the DOM
			QDomElement root = dom_tree.documentElement();

			QDomNode node = root.firstChild();

			// Cache LMMS version
			if (!root.attribute("version").isNull()) {
				m_version = root.attribute("version");
			}

			// Get the version of the configuration file (for upgrade purposes)
			if( root.attribute("configversion").isNull() )
			{
				m_configVersion = legacyConfigVersion(); // No configversion attribute found
			}
			else
			{
				bool success;
				m_configVersion = root.attribute("configversion").toUInt(&success);
				if( !success ) qWarning("Config Version conversion failure.");
			}

			// create the settings-map out of the DOM
			while(!node.isNull())
			{
				if(node.isElement() &&
					node.toElement().hasAttributes ())
				{
					stringPairVector attr;
					QDomNamedNodeMap node_attr =
						node.toElement().attributes();
					for(int i = 0; i < node_attr.count();
									++i)
					{
						QDomNode n = node_attr.item(i);
						if(n.isAttr())
						{
							attr.push_back(qMakePair(n.toAttr().name(),
											n.toAttr().value()));
						}
					}
					m_settings[node.nodeName()] = attr;
				}
				else if(node.nodeName() == "recentfiles")
				{
					m_recentlyOpenedProjects.clear();
					QDomNode n = node.firstChild();
					while(!n.isNull())
					{
						if(n.isElement() && n.toElement().hasAttributes())
						{
							m_recentlyOpenedProjects <<
									n.toElement().attribute("path");
						}
						n = n.nextSibling();
					}
				}
				node = node.nextSibling();
			}

			if(value("paths", "theme") != "")
			{
				m_themeDir = value("paths", "theme");
#ifdef LMMS_BUILD_WIN32
				// Detect a QDir/QFile hang on Windows
				// see issue #3417 on github
				bool badPath = (m_themeDir == "/" || m_themeDir == "\\");
#else
				bool badPath = false;
#endif

				if(badPath || !QDir(m_themeDir).exists() ||
						!QFile(m_themeDir + "/style.css").exists())
				{
					m_themeDir = defaultThemeDir();
				}
				m_themeDir = ensureTrailingSlash(m_themeDir);
			}
			setWorkingDir(value("paths", "workingdir"));

			setGIGDir(value("paths", "gigdir") == "" ? gigDir() : value("paths", "gigdir"));
			setSF2Dir(value("paths", "sf2dir") == "" ? sf2Dir() : value("paths", "sf2dir"));
			setVSTDir(value("paths", "vstdir"));
			setLADSPADir(value("paths", "ladspadir"));
		#ifdef LMMS_HAVE_STK
			setSTKDir(value("paths", "stkdir"));
		#endif
		#ifdef LMMS_HAVE_FLUIDSYNTH
			setSF2File(value("paths", "defaultsf2"));
		#endif
			setBackgroundPicFile(value("paths", "backgroundtheme"));
		}
		else if(gui)
		{
			QMessageBox::warning(NULL, MainWindow::tr("Configuration file"),
									MainWindow::tr("Error while parsing configuration file at line %1:%2: %3").
													arg(errorLine).
													arg(errorCol).
													arg(errorString));
		}
		cfg_file.close();
	}

	// Plugins are searched recursively, blacklist problematic locations
	if( m_vstDir.isEmpty() || m_vstDir == QDir::separator() || m_vstDir == "/" ||
			m_vstDir == ensureTrailingSlash( QDir::homePath() ) ||
			!QDir( m_vstDir ).exists() )
	{
#ifdef LMMS_BUILD_WIN32
		QString programFiles = QString::fromLocal8Bit(getenv("ProgramFiles"));
		m_vstDir =  programFiles + "/VstPlugins/";
#else
		m_vstDir =  m_workingDir + "plugins/vst/";
#endif
	}

	if(m_ladspaDir.isEmpty() )
	{
		m_ladspaDir = userLadspaDir();
	}

#ifdef LMMS_HAVE_STK
	if(m_stkDir.isEmpty() || m_stkDir == QDir::separator() || m_stkDir == "/" ||
			!QDir(m_stkDir).exists())
	{
#if defined(LMMS_BUILD_WIN32)
		m_stkDir = m_dataDir + "stk/rawwaves/";
#else
		// Look for bundled raw waves first
		m_stkDir = qApp->applicationDirPath() + "/../share/stk/rawwaves/";
		// Try system installations if not exists
		if (!QDir(m_stkDir).exists())
		{
			m_stkDir = "/usr/local/share/stk/rawwaves/";
		}
		if (!QDir(m_stkDir).exists())
		{
			m_stkDir = "/usr/share/stk/rawwaves/";
		}
#endif
	}
#endif

	upgrade();

	QStringList searchPaths;
	if(! qgetenv("LMMS_THEME_PATH").isNull())
		searchPaths << qgetenv("LMMS_THEME_PATH");
	searchPaths << themeDir() << defaultThemeDir();
	QDir::setSearchPaths("resources", searchPaths);

	// Create any missing subdirectories in the working dir, but only if the working dir exists
	if(hasWorkingDir())
	{
		createWorkingDir();
	}
}

void ConfigManager::saveConfigFile()
{
	setValue("paths", "theme", m_themeDir);
	setValue("paths", "workingdir", m_workingDir);
	setValue("paths", "vstdir", m_vstDir);
	setValue("paths", "gigdir", m_gigDir);
	setValue("paths", "sf2dir", m_sf2Dir);
	setValue("paths", "ladspadir", m_ladspaDir);
#ifdef LMMS_HAVE_STK
	setValue("paths", "stkdir", m_stkDir);
#endif
#ifdef LMMS_HAVE_FLUIDSYNTH
	setValue("paths", "defaultsf2", m_sf2File);
#endif
	setValue("paths", "backgroundtheme", m_backgroundPicFile);

	QDomDocument doc("lmms-config-file");

	QDomElement lmms_config = doc.createElement("lmms");
	lmms_config.setAttribute("version", m_version);
	lmms_config.setAttribute("configversion", m_configVersion);
	doc.appendChild(lmms_config);

	for(settingsMap::iterator it = m_settings.begin();
						it != m_settings.end(); ++it)
	{
		QDomElement n = doc.createElement(it.key());
		for(stringPairVector::iterator it2 = (*it).begin();
						it2 != (*it).end(); ++it2)
		{
			n.setAttribute((*it2).first, (*it2).second);
		}
		lmms_config.appendChild(n);
	}

	QDomElement recent_files = doc.createElement("recentfiles");

	for(QStringList::iterator it = m_recentlyOpenedProjects.begin();
				it != m_recentlyOpenedProjects.end(); ++it)
	{
		QDomElement n = doc.createElement("file");
		n.setAttribute("path", *it);
		recent_files.appendChild(n);
	}
	lmms_config.appendChild(recent_files);

	QString xml = "<?xml version=\"1.0\"?>\n" + doc.toString(2);

	QFile outfile(m_lmmsRcFile);
	if(!outfile.open(QIODevice::WriteOnly | QIODevice::Truncate))
	{
		QString title, message;
		title = MainWindow::tr("Could not open file");
		message = MainWindow::tr("Could not open file %1 "
					"for writing.\nPlease make "
					"sure you have write "
					"permission to the file and "
					"the directory containing the "
					"file and try again!"
						).arg(m_lmmsRcFile);
		if(gui)
		{
			QMessageBox::critical(NULL, title, message,
						QMessageBox::Ok,
						QMessageBox::NoButton);
		}
		return;
	}

	outfile.write(xml.toUtf8());
	outfile.close();
}

void ConfigManager::initPortableWorkingDir()
{
	QString applicationPath = qApp->applicationDirPath();
	m_workingDir = applicationPath + "/lmms-workspace/";
	m_lmmsRcFile = applicationPath + "/.lmmsrc.xml";
}

void ConfigManager::initInstalledWorkingDir()
{
	m_workingDir = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation) + "/lmms/";
	m_lmmsRcFile = QDir::home().absolutePath() +"/.lmmsrc.xml";
	// Detect < 1.2.0 working directory as a courtesy
	if ( QFileInfo( QDir::home().absolutePath() + "/lmms/projects/" ).exists() )
		m_workingDir = QDir::home().absolutePath() + "/lmms/";
}

void ConfigManager::initDevelopmentWorkingDir()
{
	// If we're in development (lmms is not installed) let's get the source and
	// binary directories by reading the CMake Cache
	QDir appPath = qApp->applicationDirPath();
	// If in tests, get parent directory
	if (appPath.dirName() == "tests") {
		appPath.cdUp();
	}
	QFile cmakeCache(appPath.absoluteFilePath("CMakeCache.txt"));
	if (cmakeCache.exists()) {
		cmakeCache.open(QFile::ReadOnly);
		QTextStream stream(&cmakeCache);

		// Find the lines containing something like lmms_SOURCE_DIR:static=<dir>
		// and lmms_BINARY_DIR:static=<dir>
		int done = 0;
		while(! stream.atEnd())
		{
			QString line = stream.readLine();

			if (line.startsWith("lmms_SOURCE_DIR:")) {
				QString srcDir = line.section('=', -1).trimmed();
				QDir::addSearchPath("data", srcDir + "/data/");
				done++;
			}
			if (line.startsWith("lmms_BINARY_DIR:")) {
				m_lmmsRcFile = line.section('=', -1).trimmed() +  QDir::separator() +
							   ".lmmsrc.xml";
				done++;
			}
			if (done == 2)
			{
				break;
			}
		}

		cmakeCache.close();
	}
}

// If configversion is not present, we will convert the LMMS version to the appropriate
// configuration file version for backwards compatibility.
unsigned int ConfigManager::legacyConfigVersion()
{
	ProjectVersion createdWith = m_version;

	createdWith.setCompareType(ProjectVersion::Build);

	if( createdWith < "1.1.90" )
	{
		return 0;
	}
	else if( createdWith < "1.1.91" )
	{
		return 1;
	}
	else
	{
		return 2;
	}
}


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

#include "Song.h"
#include "Mixer.h"
#include "ControllerConnection.h"
#include "ControllerDialog.h"
#include "LfoController.h"
#include "MidiController.h"
#include "PeakController.h"

long Controller::s_periods = 0;
QVector<Controller *> Controller::s_controllers;

Controller::Controller( ControllerTypes _type, Model * _parent,
					const QString & _display_name ) :
	Model( _parent, _display_name ),
	JournallingObject(),
	m_valueBuffer( Engine::mixer()->framesPerPeriod() ),
	m_bufferLastUpdated( -1 ),
	m_connectionCount( 0 ),
	m_type( _type )
{
	if( _type != DummyController && _type != MidiController )
	{
		s_controllers.append( this );
		// Determine which name to use
		for ( uint i=s_controllers.size(); ; i++ )
		{
			QString new_name = QString( tr( "Controller %1" ) )
					.arg( i );

			// Check if name is already in use
			bool name_used = false;
			for (Controller * controller : s_controllers)
			{
				if ( controller->name() == new_name )
				{
					name_used = true;
					break;
				}
			}
			if ( ! name_used )
			{
				m_name = new_name;
				break;
			}
		}
	}
	updateValueBuffer();
}

Controller::~Controller()
{
	int idx = s_controllers.indexOf( this );
	if( idx >= 0 )
	{
		s_controllers.remove( idx );
	}

	m_valueBuffer.clear();
	// Remove connections by destroyed signal
}

// Get current value, with an offset into the current buffer for sample exactness
float Controller::currentValue( int _offset )
{
	if( _offset == 0 || isSampleExact() )
	{
		m_currentValue = fittedValue( value( _offset ) );
	}

	return m_currentValue;
}

float Controller::value( int offset )
{
	if( m_bufferLastUpdated != s_periods )
	{
		updateValueBuffer();
	}
	return m_valueBuffer.values()[ offset ];
}

ValueBuffer * Controller::valueBuffer()
{
	if( m_bufferLastUpdated != s_periods )
	{
		updateValueBuffer();
	}
	return &m_valueBuffer;
}

void Controller::updateValueBuffer()
{
	m_valueBuffer.fill(0.5f);
	m_bufferLastUpdated = s_periods;
}

// Get position in frames
unsigned int Controller::runningFrames()
{
	return s_periods * Engine::mixer()->framesPerPeriod();
}

// Get position in seconds
float Controller::runningTime()
{
	return runningFrames() / Engine::mixer()->processingSampleRate();
}

void Controller::triggerFrameCounter()
{
	for (Controller * controller : s_controllers)
	{
		// This signal is for updating values for both stubborn knobs and for
		// painting.  If we ever get all the widgets to use or at least check
		// currentValue() then we can throttle the signal and only use it for
		// GUI.
		emit controller->valueChanged();
	}

	s_periods ++;
	//emit s_signaler.triggerValueChanged();
}

void Controller::resetFrameCounter()
{
	for (Controller * controller : s_controllers)
	{
		controller->m_bufferLastUpdated = 0;
	}
	s_periods = 0;
}

Controller * Controller::create( ControllerTypes _ct, Model * _parent )
{
	static Controller * dummy = NULL;
	Controller * c = NULL;

	switch( _ct )
	{
		case Controller::DummyController:
			if (!dummy)
				dummy = new Controller( DummyController, NULL,
								QString() );
			c = dummy;
			break;

		case Controller::LfoController:
			c = new ::LfoController( _parent );
			break;

		case Controller::PeakController:
			//Already instantiated in EffectChain::loadSettings()
			Q_ASSERT( false );
			break;

		case Controller::MidiController:
			c = new ::MidiController( _parent );
			break;

		default:
			break;
	}

	return( c );
}

Controller * Controller::create( const QDomElement & _this, Model * _parent )
{
	Controller * c;
	if( _this.attribute( "type" ).toInt() == Controller::PeakController )
	{
		c = PeakController::getControllerBySetting( _this );
	}
	else
	{
		c = create(
			static_cast<ControllerTypes>( _this.attribute( "type" ).toInt() ),
										_parent );
	}

	if( c != NULL )
	{
		c->restoreState( _this );
	}

	return( c );
}

bool Controller::hasModel( const Model * m ) const
{
	for (QObject * c : children())
	{
		AutomatableModel * am = qobject_cast<AutomatableModel*>(c);
		if( am != NULL )
		{
			if( am == m )
			{
				return true;
			}

			ControllerConnection * cc = am->controllerConnection();
			if( cc != NULL && cc->getController()->hasModel( m ) )
			{
				return true;
			}
		}
	}

	return false;
}

void Controller::saveSettings( QDomDocument & _doc, QDomElement & _this )
{
	_this.setAttribute( "type", type() );
	_this.setAttribute( "name", name() );
}

void Controller::loadSettings( const QDomElement & _this )
{
	if( _this.attribute( "type" ).toInt() != type() )
	{
		qWarning( "controller-type does not match controller-type of "
							"settings-node!\n" );
	}

	setName( _this.attribute( "name" ) );
}

QString Controller::nodeName() const
{
	return( "Controller" );
}

ControllerDialog * Controller::createDialog( QWidget * _parent )
{
	ControllerDialog * d = new ControllerDialog( this, _parent );

	return d;
}

void Controller::addConnection( ControllerConnection * )
{
	m_connectionCount++;
}

void Controller::removeConnection( ControllerConnection * )
{
	m_connectionCount--;
	Q_ASSERT( m_connectionCount >= 0 );
}

int Controller::connectionCount() const{
	return m_connectionCount;
}

repo_name: CYBERDEViL/LMMS

license: mit


code:
/*
 * ControllerConnection.cpp - implementation of class controller connection
 *            which handles the link between AutomatableModels and controllers
 *
 * Copyright (c) 2008 Paul Giblock <drfaygo/at/gmail.com>
 * Copyright (c) 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 <QObject>

#include "Song.h"
#include "ControllerConnection.h"

ControllerConnectionVector ControllerConnection::s_connections;

ControllerConnection::ControllerConnection( Controller * _controller ) :
	m_controller( NULL ),
	m_controllerId( -1 ),
	m_ownsController( false )
{
	if( _controller != NULL )
	{
		setController( _controller );
	}
	else
	{
		m_controller = Controller::create( Controller::DummyController,
									NULL );
	}
	s_connections.append( this );
}

ControllerConnection::ControllerConnection( int _controllerId ) :
	m_controller( Controller::create( Controller::DummyController, NULL ) ),
	m_controllerId( _controllerId ),
	m_ownsController( false )
{
	s_connections.append( this );
}

ControllerConnection::~ControllerConnection()
{
	if( m_controller && m_controller->type() != Controller::DummyController )
	{
		m_controller->removeConnection( this );
	}
	s_connections.remove( s_connections.indexOf( this ) );
	if( m_ownsController )
	{
		delete m_controller;
	}
}

void ControllerConnection::setController( int /*_controllerId*/ )
{
}

void ControllerConnection::setController( Controller * _controller )
{
	if( m_ownsController && m_controller )
	{
		delete m_controller;
		m_controller = NULL;
	}

	if( m_controller && m_controller->type() != Controller::DummyController )
	{
		m_controller->removeConnection( this );
	}

	if( !_controller )
	{
		m_controller = Controller::create( Controller::DummyController, NULL );
	}
	else
	{
		m_controller = _controller;
	}
	m_controllerId = -1;

	if( _controller->type() != Controller::DummyController )
	{
		_controller->addConnection( this );
		QObject::connect( _controller, SIGNAL( valueChanged() ),
				this, SIGNAL( valueChanged() ), Qt::DirectConnection );
	}

	m_ownsController =
			( _controller->type() == Controller::MidiController );

	// If we don't own the controller, allow deletion of controller
	// to delete the connection
	if( !m_ownsController ) {
		QObject::connect( _controller, SIGNAL( destroyed() ),
				this, SLOT( deleteConnection() ) );
	}
}

inline void ControllerConnection::setTargetName( const QString & _name )
{
	m_targetName = _name;
	if( m_controller )
	{
	//	m_controller->getMidiPort()->setName( _name );
	}
}

/*
 * A connection may not be finalized.  This means, the connection should exist,
 * but the controller does not yet exist.  This happens when loading.  Even
 * loading connections last won't help, since there can be connections BETWEEN
 * controllers. So, we remember the controller-ID and use a dummyController
 * instead.  Once the song is loaded, finalizeConnections() connects to the proper controllers
 */
void ControllerConnection::finalizeConnections()
{
	for( int i = 0; i < s_connections.size(); ++i )
	{
		ControllerConnection * c = s_connections[i];
		if ( !c->isFinalized() && c->m_controllerId <
				Engine::getSong()->controllers().size() )
		{
			c->setController( Engine::getSong()->
					controllers().at( c->m_controllerId ) );
		}
		else if (c->getController()->type() == Controller::DummyController)
		{
			delete c;
			--i;
		}
	}
}

void ControllerConnection::saveSettings( QDomDocument & _doc, QDomElement & _this )
{
	if( Engine::getSong() )
	{
		if( m_ownsController )
		{
			m_controller->saveState( _doc, _this );
		}
		else
		{
			int id = Engine::getSong()->controllers().indexOf( m_controller );
			if( id >= 0 )
			{
				_this.setAttribute( "id", id );
			}
		}
	}
}

void ControllerConnection::loadSettings( const QDomElement & _this )
{
	QDomNode node = _this.firstChild();
	if( !node.isNull() )
	{
		setController( Controller::create( node.toElement(), Engine::getSong() ) );
	}
	else
	{
		m_controllerId = _this.attribute( "id", "-1" ).toInt();
		if( m_controllerId < 0 )
		{
			qWarning( "controller index invalid\n" );
			m_controllerId = -1;
		}

		if (!Engine::getSong()->isLoadingProject()
			&& m_controllerId != -1
			&& m_controllerId < Engine::getSong()->controllers().size())
		{
			setController( Engine::getSong()->
					controllers().at( m_controllerId ) );
		}
		else
		{
			m_controller = Controller::create( Controller::DummyController, NULL );
		}
	}
}

void ControllerConnection::deleteConnection()
{
	delete this;
}


code:
/*
 * DataFile.cpp - implementation of class DataFile
 *
 * Copyright (c) 2004-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
 * Copyright (c) 2012-2013 Paul Giblock    <p/at/pgiblock.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 "DataFile.h"

#include <math.h>

#include <QDebug>
#include <QFile>
#include <QFileInfo>
#include <QMessageBox>

#include "base64.h"
#include "ConfigManager.h"
#include "Effect.h"
#include "embed.h"
#include "GuiApplication.h"
#include "LocaleHelper.h"
#include "PluginFactory.h"
#include "ProjectVersion.h"
#include "SongEditor.h"
#include "TextFloat.h"

#include "lmmsversion.h"

static void findIds(const QDomElement& elem, QList<jo_id_t>& idList);

// Vector with all the upgrade methods
const std::vector<DataFile::UpgradeMethod> DataFile::UPGRADE_METHODS = {
	&DataFile::upgrade_0_2_1_20070501   ,   &DataFile::upgrade_0_2_1_20070508,
	&DataFile::upgrade_0_3_0_rc2        ,   &DataFile::upgrade_0_3_0,
	&DataFile::upgrade_0_4_0_20080104   ,   &DataFile::upgrade_0_4_0_20080118,
	&DataFile::upgrade_0_4_0_20080129   ,   &DataFile::upgrade_0_4_0_20080409,
	&DataFile::upgrade_0_4_0_20080607   ,   &DataFile::upgrade_0_4_0_20080622,
	&DataFile::upgrade_0_4_0_beta1      ,   &DataFile::upgrade_0_4_0_rc2,
	&DataFile::upgrade_1_0_99           ,   &DataFile::upgrade_1_1_0,
	&DataFile::upgrade_1_1_91           ,   &DataFile::upgrade_1_2_0_rc3,
	&DataFile::upgrade_1_3_0            ,   &DataFile::upgrade_noHiddenClipNames
};

// Vector of all versions that have upgrade routines.
const std::vector<ProjectVersion> DataFile::UPGRADE_VERSIONS = {
	"0.2.1-20070501"   ,   "0.2.1-20070508"   ,   "0.3.0-rc2",
	"0.3.0"            ,   "0.4.0-20080104"   ,   "0.4.0-20080118",
	"0.4.0-20080129"   ,   "0.4.0-20080409"   ,   "0.4.0-20080607",
	"0.4.0-20080622"   ,   "0.4.0-beta1"      ,   "0.4.0-rc2",
	"1.0.99-0"         ,   "1.1.0-0"          ,   "1.1.91-0",
	"1.2.0-rc3"        ,   "1.3.0"
};

DataFile::typeDescStruct
		DataFile::s_types[DataFile::TypeCount] =
{
	{ DataFile::UnknownType, "unknown" },
	{ DataFile::SongProject, "song" },
	{ DataFile::SongProjectTemplate, "songtemplate" },
	{ DataFile::InstrumentTrackSettings, "instrumenttracksettings" },
	{ DataFile::DragNDropData, "dnddata" },
	{ DataFile::ClipboardData, "clipboard-data" },
	{ DataFile::JournalData, "journaldata" },
	{ DataFile::EffectSettings, "effectsettings" }
} ;

DataFile::DataFile( Type type ) :
	QDomDocument( "lmms-project" ),
	m_content(),
	m_head(),
	m_type( type ),
	m_fileVersion( UPGRADE_METHODS.size() )
{
	appendChild( createProcessingInstruction("xml", "version=\"1.0\""));
	QDomElement root = createElement( "lmms-project" );
	root.setAttribute( "version", m_fileVersion );
	root.setAttribute( "type", typeName( type ) );
	root.setAttribute( "creator", "LMMS" );
	root.setAttribute( "creatorversion", LMMS_VERSION );
	appendChild( root );

	m_head = createElement( "head" );
	root.appendChild( m_head );

	m_content = createElement( typeName( type ) );
	root.appendChild( m_content );

}

DataFile::DataFile( const QString & _fileName ) :
	QDomDocument(),
	m_content(),
	m_head(),
	m_fileVersion( UPGRADE_METHODS.size() )
{
	QFile inFile( _fileName );
	if( !inFile.open( QIODevice::ReadOnly ) )
	{
		if( gui )
		{
			QMessageBox::critical( NULL,
				SongEditor::tr( "Could not open file" ),
				SongEditor::tr( "Could not open file %1. You probably "
						"have no permissions to read this "
						"file.\n Please make sure to have at "
						"least read permissions to the file "
						"and try again." ).arg( _fileName ) );
		}

		return;
	}

	loadData( inFile.readAll(), _fileName );
}

DataFile::DataFile( const QByteArray & _data ) :
	QDomDocument(),
	m_content(),
	m_head(),
	m_fileVersion( UPGRADE_METHODS.size() )
{
	loadData( _data, "<internal data>" );
}

DataFile::~DataFile()
{
}

bool DataFile::validate( QString extension )
{
	switch( m_type )
	{
	case Type::SongProject:
		if( extension == "mmp" || extension == "mmpz" )
		{
			return true;
		}
		break;
	case Type::SongProjectTemplate:
		if(  extension == "mpt" )
		{
			return true;
		}
		break;
	case Type::InstrumentTrackSettings:
		if ( extension == "xpf" || extension == "xml" )
		{
			return true;
		}
		break;
	case Type::UnknownType:
		if (! ( extension == "mmp" || extension == "mpt" || extension == "mmpz" ||
				extension == "xpf" || extension == "xml" ||
				( extension == "xiz" && ! pluginFactory->pluginSupportingExtension(extension).isNull()) ||
				extension == "sf2" || extension == "sf3" || extension == "pat" || extension == "mid" ||
				extension == "dll"
#ifdef LMMS_HAVE_LV2
				|| extension == "lv2"
#endif
				) )
		{
			return true;
		}
		if( extension == "wav" || extension == "ogg" ||
				extension == "ds" )
		{
			return true;
		}
		break;
	default:
		return false;
	}
	return false;
}

QString DataFile::nameWithExtension( const QString & _fn ) const
{
	switch( type() )
	{
		case SongProject:
			if( _fn.section( '.', -1 ) != "mmp" &&
					_fn.section( '.', -1 ) != "mpt" &&
					_fn.section( '.', -1 ) != "mmpz" )
			{
				if( ConfigManager::inst()->value( "app",
						"nommpz" ).toInt() == 0 )
				{
					return _fn + ".mmpz";
				}
				return _fn + ".mmp";
			}
			break;
		case SongProjectTemplate:
			if( _fn.section( '.',-1 ) != "mpt" )
			{
				return _fn + ".mpt";
			}
			break;
		case InstrumentTrackSettings:
			if( _fn.section( '.', -1 ) != "xpf" )
			{
				return _fn + ".xpf";
			}
			break;
		default: ;
	}
	return _fn;
}

void DataFile::write( QTextStream & _strm )
{
	if( type() == SongProject || type() == SongProjectTemplate
					|| type() == InstrumentTrackSettings )
	{
		cleanMetaNodes( documentElement() );
	}

	save(_strm, 2);
}

bool DataFile::writeFile( const QString& filename )
{
	const QString fullName = nameWithExtension( filename );
	const QString fullNameTemp = fullName + ".new";
	const QString fullNameBak = fullName + ".bak";

	QFile outfile( fullNameTemp );

	if( !outfile.open( QIODevice::WriteOnly | QIODevice::Truncate ) )
	{
		if( gui )
		{
			QMessageBox::critical( NULL,
				SongEditor::tr( "Could not write file" ),
				SongEditor::tr( "Could not open %1 for writing. You probably are not permitted to "
								"write to this file. Please make sure you have write-access to "
								"the file and try again." ).arg( fullName ) );
		}

		return false;
	}

	if( fullName.section( '.', -1 ) == "mmpz" )
	{
		QString xml;
		QTextStream ts( &xml );
		write( ts );
		outfile.write( qCompress( xml.toUtf8() ) );
	}
	else
	{
		QTextStream ts( &outfile );
		write( ts );
	}

	outfile.close();

	// make sure the file has been written correctly
	if( QFileInfo( outfile.fileName() ).size() > 0 )
	{
		if( ConfigManager::inst()->value( "app", "disablebackup" ).toInt() )
		{
			// remove current file
			QFile::remove( fullName );
		}
		else
		{
			// remove old backup file
			QFile::remove( fullNameBak );
			// move current file to backup file
			QFile::rename( fullName, fullNameBak );
		}
		// move temporary file to current file
		QFile::rename( fullNameTemp, fullName );

		return true;
	}

	return false;
}

DataFile::Type DataFile::type( const QString& typeName )
{
	for( int i = 0; i < TypeCount; ++i )
	{
		if( s_types[i].m_name == typeName )
		{
			return static_cast<DataFile::Type>( i );
		}
	}

	// compat code
	if( typeName == "channelsettings" )
	{
		return DataFile::InstrumentTrackSettings;
	}

	return UnknownType;
}

QString DataFile::typeName( Type type )
{
	if( type >= UnknownType && type < TypeCount )
	{
		return s_types[type].m_name;
	}

	return s_types[UnknownType].m_name;
}

void DataFile::cleanMetaNodes( QDomElement _de )
{
	QDomNode node = _de.firstChild();
	while( !node.isNull() )
	{
		if( node.isElement() )
		{
			if( node.toElement().attribute( "metadata" ).toInt() )
			{
				QDomNode ns = node.nextSibling();
				_de.removeChild( node );
				node = ns;
				continue;
			}
			if( node.hasChildNodes() )
			{
				cleanMetaNodes( node.toElement() );
			}
		}
		node = node.nextSibling();
	}
}

void DataFile::upgrade_0_2_1_20070501()
{
	// Upgrade to version 0.2.1-20070501
	QDomNodeList list = elementsByTagName( "arpandchords" );
	for( int i = 0; !list.item( i ).isNull(); ++i )
	{
		QDomElement el = list.item( i ).toElement();
		if( el.hasAttribute( "arpdir" ) )
		{
			int arpdir = el.attribute( "arpdir" ).toInt();
			if( arpdir > 0 )
			{
				el.setAttribute( "arpdir", arpdir - 1 );
			}
			else
			{
				el.setAttribute( "arpdisabled", "1" );
			}
		}
	}

	list = elementsByTagName( "sampletrack" );
	for( int i = 0; !list.item( i ).isNull(); ++i )
	{
		QDomElement el = list.item( i ).toElement();
		if( el.attribute( "vol" ) != "" )
		{
			el.setAttribute( "vol", LocaleHelper::toFloat(
					el.attribute( "vol" ) ) * 100.0f );
		}
		else
		{
			QDomNode node = el.namedItem(
						"automation-pattern" );
			if( !node.isElement() ||
				!node.namedItem( "vol" ).isElement() )
			{
				el.setAttribute( "vol", 100.0f );
			}
		}
	}

	list = elementsByTagName( "ladspacontrols" );
	for( int i = 0; !list.item( i ).isNull(); ++i )
	{
		QDomElement el = list.item( i ).toElement();
		QDomNode anode = el.namedItem( "automation-pattern" );
		QDomNode node = anode.firstChild();
		while( !node.isNull() )
		{
			if( node.isElement() )
			{
				QString name = node.nodeName();
				if( name.endsWith( "link" ) )
				{
					el.setAttribute( name,
						node.namedItem( "time" )
						.toElement()
						.attribute( "value" ) );
					QDomNode oldNode = node;
					node = node.nextSibling();
					anode.removeChild( oldNode );
					continue;
				}
			}
			node = node.nextSibling();
		}
	}

	QDomNode node = m_head.firstChild();
	while( !node.isNull() )
	{
		if( node.isElement() )
		{
			if( node.nodeName() == "bpm" )
			{
				int value = node.toElement().attribute(
						"value" ).toInt();
				if( value > 0 )
				{
					m_head.setAttribute( "bpm",
								value );
					QDomNode oldNode = node;
					node = node.nextSibling();
					m_head.removeChild( oldNode );
					continue;
				}
			}
			else if( node.nodeName() == "mastervol" )
			{
				int value = node.toElement().attribute(
						"value" ).toInt();
				if( value > 0 )
				{
					m_head.setAttribute(
						"mastervol", value );
					QDomNode oldNode = node;
					node = node.nextSibling();
					m_head.removeChild( oldNode );
					continue;
				}
			}
			else if( node.nodeName() == "masterpitch" )
			{
				m_head.setAttribute( "masterpitch",
					-node.toElement().attribute(
						"value" ).toInt() );
				QDomNode oldNode = node;
				node = node.nextSibling();
				m_head.removeChild( oldNode );
				continue;
			}
		}
		node = node.nextSibling();
	}
}

void DataFile::upgrade_0_2_1_20070508()
{
	// Upgrade to version 0.2.1-20070508 from some version greater than or equal to 0.2.1-20070501
	QDomNodeList list = elementsByTagName( "arpandchords" );
	for( int i = 0; !list.item( i ).isNull(); ++i )
	{
		QDomElement el = list.item( i ).toElement();
		if( el.hasAttribute( "chorddisabled" ) )
		{
			el.setAttribute( "chord-enabled",
				!el.attribute( "chorddisabled" )
							.toInt() );
			el.setAttribute( "arp-enabled",
				!el.attribute( "arpdisabled" )
							.toInt() );
		}
		else if( !el.hasAttribute( "chord-enabled" ) )
		{
			el.setAttribute( "chord-enabled", true );
			el.setAttribute( "arp-enabled",
				el.attribute( "arpdir" ).toInt() != 0 );
		}
	}

	while( !( list = elementsByTagName( "channeltrack" ) ).isEmpty() )
	{
		QDomElement el = list.item( 0 ).toElement();
		el.setTagName( "instrumenttrack" );
	}

	list = elementsByTagName( "instrumenttrack" );
	for( int i = 0; !list.item( i ).isNull(); ++i )
	{
		QDomElement el = list.item( i ).toElement();
		if( el.hasAttribute( "vol" ) )
		{
			float value = LocaleHelper::toFloat( el.attribute( "vol" ) );
			value = roundf( value * 0.585786438f );
			el.setAttribute( "vol", value );
		}
		else
		{
			QDomNodeList vol_list = el.namedItem(
						"automation-pattern" )
					.namedItem( "vol" ).toElement()
					.elementsByTagName( "time" );
			for( int j = 0; !vol_list.item( j ).isNull();
								++j )
			{
				QDomElement timeEl = list.item( j )
							.toElement();
				int value = timeEl.attribute( "value" )
							.toInt();
				value = (int)roundf( value *
							0.585786438f );
				timeEl.setAttribute( "value", value );
			}
		}
	}
}

void DataFile::upgrade_0_3_0_rc2()
{
	// Upgrade to version 0.3.0-rc2 from some version greater than or equal to 0.2.1-20070508
	QDomNodeList list = elementsByTagName( "arpandchords" );
	for( int i = 0; !list.item( i ).isNull(); ++i )
	{
		QDomElement el = list.item( i ).toElement();
		if( el.attribute( "arpdir" ).toInt() > 0 )
		{
			el.setAttribute( "arpdir",
				el.attribute( "arpdir" ).toInt() - 1 );
		}
	}
}

void DataFile::upgrade_0_3_0()
{
	// Upgrade to version 0.3.0 (final) from some version greater than or equal to 0.3.0-rc2
	QDomNodeList list;
	while( !( list = elementsByTagName(
				"pluckedstringsynth" ) ).isEmpty() )
	{
		QDomElement el = list.item( 0 ).toElement();
		el.setTagName( "vibedstrings" );
		el.setAttribute( "active0", 1 );
	}

	while( !( list = elementsByTagName( "lb303" ) ).isEmpty() )
	{
		QDomElement el = list.item( 0 ).toElement();
		el.setTagName( "lb302" );
	}

	while( !( list = elementsByTagName( "channelsettings" ) ).
							isEmpty() )
	{
		QDomElement el = list.item( 0 ).toElement();
		el.setTagName( "instrumenttracksettings" );
	}
}

void DataFile::upgrade_0_4_0_20080104()
{
	// Upgrade to version 0.4.0-20080104 from some version greater than or equal to 0.3.0 (final)
	QDomNodeList list = elementsByTagName( "fx" );
	for( int i = 0; !list.item( i ).isNull(); ++i )
	{
		QDomElement el = list.item( i ).toElement();
		if( el.hasAttribute( "fxdisabled" ) &&
			el.attribute( "fxdisabled" ).toInt() == 0 )
		{
			el.setAttribute( "enabled", 1 );
		}
	}
}

void DataFile::upgrade_0_4_0_20080118()
{
	// Upgrade to version 0.4.0-20080118 from some version greater than or equal to 0.4.0-20080104
	QDomNodeList list;
	while( !( list = elementsByTagName( "fx" ) ).isEmpty() )
	{
		QDomElement fxchain = list.item( 0 ).toElement();
		fxchain.setTagName( "fxchain" );
		QDomNode rack = list.item( 0 ).firstChild();
		QDomNodeList effects = rack.childNodes();
		// move items one level up
		while( effects.count() )
		{
			fxchain.appendChild( effects.at( 0 ) );
		}
		fxchain.setAttribute( "numofeffects",
			rack.toElement().attribute( "numofeffects" ) );
		fxchain.removeChild( rack );
	}
}

void DataFile::upgrade_0_4_0_20080129()
{
	// Upgrade to version 0.4.0-20080129 from some version greater than or equal to 0.4.0-20080118
	QDomNodeList list;
	while( !( list =
		elementsByTagName( "arpandchords" ) ).isEmpty() )
	{
		QDomElement aac = list.item( 0 ).toElement();
		aac.setTagName( "arpeggiator" );
		QDomNode cloned = aac.cloneNode();
		cloned.toElement().setTagName( "chordcreator" );
		aac.parentNode().appendChild( cloned );
	}
}

void DataFile::upgrade_0_4_0_20080409()
{
	// Upgrade to version 0.4.0-20080409 from some version greater than or equal to 0.4.0-20080129
	QStringList s;
	s << "note" << "pattern" << "bbtco" << "sampletco" << "time";
	for( QStringList::iterator it = s.begin(); it < s.end(); ++it )
	{
		QDomNodeList list = elementsByTagName( *it );
		for( int i = 0; !list.item( i ).isNull(); ++i )
		{
			QDomElement el = list.item( i ).toElement();
			el.setAttribute( "pos",
				el.attribute( "pos" ).toInt()*3 );
			el.setAttribute( "len",
				el.attribute( "len" ).toInt()*3 );
		}
	}
	QDomNodeList list = elementsByTagName( "timeline" );
	for( int i = 0; !list.item( i ).isNull(); ++i )
	{
		QDomElement el = list.item( i ).toElement();
		el.setAttribute( "lp0pos",
			el.attribute( "lp0pos" ).toInt()*3 );
		el.setAttribute( "lp1pos",
			el.attribute( "lp1pos" ).toInt()*3 );
	}
}

void DataFile::upgrade_0_4_0_20080607()
{
	// Upgrade to version 0.4.0-20080607 from some version greater than or equal to 0.3.0-20080409
	QDomNodeList list;
	while( !( list = elementsByTagName( "midi" ) ).isEmpty() )
	{
		QDomElement el = list.item( 0 ).toElement();
		el.setTagName( "midiport" );
	}
}

void DataFile::upgrade_0_4_0_20080622()
{
	// Upgrade to version 0.4.0-20080622 from some version greater than or equal to 0.3.0-20080607
	QDomNodeList list;
	while( !( list = elementsByTagName(
				"automation-pattern" ) ).isEmpty() )
	{
		QDomElement el = list.item( 0 ).toElement();
		el.setTagName( "automationpattern" );
	}

	list = elementsByTagName( "bbtrack" );
	for( int i = 0; !list.item( i ).isNull(); ++i )
	{
		QDomElement el = list.item( i ).toElement();
		QString s = el.attribute( "name" );
		s.replace( QRegExp( "^Beat/Baseline " ),
						"Beat/Bassline " );
		el.setAttribute( "name", s );
	}
}

void DataFile::upgrade_0_4_0_beta1()
{
	// Upgrade to version 0.4.0-beta1 from some version greater than or equal to 0.4.0-20080622
	// convert binary effect-key-blobs to XML
	QDomNodeList list;
	list = elementsByTagName( "effect" );
	for( int i = 0; !list.item( i ).isNull(); ++i )
	{
		QDomElement el = list.item( i ).toElement();
		QString k = el.attribute( "key" );
		if( !k.isEmpty() )
		{
			const QList<QVariant> l =
				base64::decode( k, QVariant::List ).toList();
			if( !l.isEmpty() )
			{
				QString name = l[0].toString();
				QVariant u = l[1];
				EffectKey::AttributeMap m;
				// VST-effect?
				if( u.type() == QVariant::String )
				{
					m["file"] = u.toString();
				}
				// LADSPA-effect?
				else if( u.type() == QVariant::StringList )
				{
					const QStringList sl = u.toStringList();
					m["plugin"] = sl.value( 0 );
					m["file"] = sl.value( 1 );
				}
				EffectKey key( NULL, name, m );
				el.appendChild( key.saveXML( *this ) );
			}
		}
	}
}

void DataFile::upgrade_0_4_0_rc2()
{
	// Upgrade to version 0.4.0-rc2 from some version greater than or equal to 0.4.0-beta1
	QDomNodeList list = elementsByTagName( "audiofileprocessor" );
	for( int i = 0; !list.item( i ).isNull(); ++i )
	{
		QDomElement el = list.item( i ).toElement();
		QString s = el.attribute( "src" );
		s.replace( "drumsynth/misc ", "drumsynth/misc_" );
		s.replace( "drumsynth/r&b", "drumsynth/r_n_b" );
		s.replace( "drumsynth/r_b", "drumsynth/r_n_b" );
		el.setAttribute( "src", s );
	}
	list = elementsByTagName( "lb302" );
	for( int i = 0; !list.item( i ).isNull(); ++i )
	{
		QDomElement el = list.item( i ).toElement();
		int s = el.attribute( "shape" ).toInt();
		if( s >= 1 )
		{
			s--;
		}
		el.setAttribute( "shape", QString("%1").arg(s) );
	}
}

void DataFile::upgrade_1_0_99()
{
	jo_id_t last_assigned_id = 0;

	QList<jo_id_t> idList;
	findIds(documentElement(), idList);

	QDomNodeList list = elementsByTagName("ladspacontrols");
	for(int i = 0; !list.item(i).isNull(); ++i)
	{
		for(QDomNode node = list.item(i).firstChild(); !node.isNull();
			node = node.nextSibling())
		{
			QDomElement el = node.toElement();
			QDomNode data_child = el.namedItem("data");
			if(!data_child.isElement())
			{
				if (el.attribute("scale_type") == "log")
				{
					QDomElement me = createElement("data");
					me.setAttribute("value", el.attribute("data"));
					me.setAttribute("scale_type", "log");

					jo_id_t id;
					for(id = last_assigned_id + 1;
						idList.contains(id); id++)
					{
					}

					last_assigned_id = id;
					idList.append(id);
					me.setAttribute("id", id);
					el.appendChild(me);

				}
			}
		}
	}
}

void DataFile::upgrade_1_1_0()
{
	QDomNodeList list = elementsByTagName("fxchannel");
	for (int i = 1; !list.item(i).isNull(); ++i)
	{
		QDomElement el = list.item(i).toElement();
		QDomElement send = createElement("send");
		send.setAttribute("channel", "0");
		send.setAttribute("amount", "1");
		el.appendChild(send);
	}
}

void DataFile::upgrade_1_1_91()
{
	// Upgrade to version 1.1.91 from some version less than 1.1.91
	QDomNodeList list = elementsByTagName( "audiofileprocessor" );
	for( int i = 0; !list.item( i ).isNull(); ++i )
	{
		QDomElement el = list.item( i ).toElement();
		QString s = el.attribute( "src" );
		s.replace( QRegExp("/samples/bassloopes/"), "/samples/bassloops/" );
		el.setAttribute( "src", s );
	}

	list = elementsByTagName( "attribute" );
	for( int i = 0; !list.item( i ).isNull(); ++i )
	{
		QDomElement el = list.item( i ).toElement();
		if( el.attribute( "name" ) == "plugin" && el.attribute( "value" ) == "vocoder-lmms" ) {
			el.setAttribute( "value", "vocoder" );
		}
	}

	list = elementsByTagName( "crossoevereqcontrols" );
	for( int i = 0; !list.item( i ).isNull(); ++i )
	{
		QDomElement el = list.item( i ).toElement();
		// invert the mute LEDs
		for( int j = 1; j <= 4; ++j ){
			QString a = QString( "mute%1" ).arg( j );
			el.setAttribute( a, ( el.attribute( a ) == "0" ) ? "1" : "0" );
		}
	}

	list = elementsByTagName( "arpeggiator" );
	for( int i = 0; !list.item( i ).isNull(); ++i )
	{
		QDomElement el = list.item( i ).toElement();
		// Swap elements ArpDirRandom and ArpDirDownAndUp
		if( el.attribute( "arpdir" ) == "3" )
		{
			el.setAttribute( "arpdir", "4" );
		}
		else if( el.attribute( "arpdir" ) == "4" )
		{
			el.setAttribute( "arpdir", "3" );
		}
	}
}

static void upgradeElement_1_2_0_rc2_42( QDomElement & el )
{
	if( el.hasAttribute( "syncmode" ) )
	{
		int syncmode = el.attribute( "syncmode" ).toInt();
		QStringList names;
		QDomNamedNodeMap atts = el.attributes();
		for( uint i = 0; i < atts.length(); i++ )
		{
			QString name = atts.item( i ).nodeName();
			if( name.endsWith( "_numerator" ) )
			{
				names << name.remove( "_numerator" )
								+ "_syncmode";
			}
		}
		for( QStringList::iterator it = names.begin(); it < names.end();
									++it )
		{
			el.setAttribute( *it, syncmode );
		}
	}

	QDomElement child = el.firstChildElement();
	while ( !child.isNull() )
	{
		upgradeElement_1_2_0_rc2_42( child );
		child = child.nextSiblingElement();
	}
}

void DataFile::upgrade_1_2_0_rc3()
{
	// Upgrade from earlier bbtrack beat note behaviour of adding
	// steps if a note is placed after the last step.
	QDomNodeList bbtracks = elementsByTagName( "bbtrack" );
	for( int i = 0; !bbtracks.item( i ).isNull(); ++i )
	{
		QDomNodeList patterns = bbtracks.item( i
				).toElement().elementsByTagName(
								"pattern" );
		for( int j = 0; !patterns.item( j ).isNull(); ++j )
		{
			int patternLength, steps;
			QDomElement el = patterns.item( j ).toElement();
			if( el.attribute( "len" ) != "" )
			{
				patternLength = el.attribute( "len" ).toInt();
				steps = patternLength / 12;
				el.setAttribute( "steps", steps );
			}
		}
	}

	// DataFile::upgrade_1_2_0_rc2_42
	QDomElement el = firstChildElement();
	while ( !el.isNull() )
	{
		upgradeElement_1_2_0_rc2_42( el );
		el = el.nextSiblingElement();
	}
}

/**
 *  Helper function to call a functor for all effect ports' DomElements,
 *  providing the functor with lists to add and remove DomElements. Helpful for
 *  patching port values from savefiles.
 */
template<class Ftor>
void iterate_ladspa_ports(QDomElement& effect, Ftor& ftor)
{
	// Head back up the DOM to upgrade ports
	QDomNodeList ladspacontrols = effect.elementsByTagName( "ladspacontrols" );
	for( int m = 0; !ladspacontrols.item( m ).isNull(); ++m )
	{
		QList<QDomElement> addList, removeList;
		QDomElement ladspacontrol = ladspacontrols.item( m ).toElement();
		for( QDomElement port = ladspacontrol.firstChild().toElement();
			!port.isNull(); port = port.nextSibling().toElement() )
		{
			QStringList parts = port.tagName().split("port");
			// Not a "port"
			if ( parts.size() < 2 )
			{
				continue;
			}
			int num = parts[1].toInt();

			// From Qt's docs of QDomNode:
			// * copying a QDomNode is OK, they still have the same
			//   pointer to the "internal" QDomNodePrivate.
			// * Also, they are using linked lists, which means
			//   deleting or appending QDomNode does not invalidate
			//   any other pointers.
			// => Inside ftor, you can (and should) push back the
			//    QDomElements by value, not references
			// => The loops below for adding and removing don't
			//    invalidate any other QDomElements
			ftor(port, num, addList, removeList);
		}

		// Add ports marked for adding
		for ( QDomElement e : addList )
		{
			ladspacontrol.appendChild( e );
		}
		// Remove ports marked for removal
		for ( QDomElement e : removeList )
		{
			ladspacontrol.removeChild( e );
		}
	}
}

// helper function if you need to print a QDomNode
QDebug operator<<(QDebug dbg, const QDomNode& node)
{
	QString s;
	QTextStream str(&s, QIODevice::WriteOnly);
	node.save(str, 2);
	dbg << qPrintable(s);
	return dbg;
}

void DataFile::upgrade_1_3_0()
{
	QDomNodeList list = elementsByTagName( "instrument" );
	for( int i = 0; !list.item( i ).isNull(); ++i )
	{
		QDomElement el = list.item( i ).toElement();
		if( el.attribute( "name" ) == "papu" )
		{
			el.setAttribute( "name", "freeboy" );
			QDomNodeList children = el.elementsByTagName( "papu" );
			for( int j = 0; !children.item( j ).isNull(); ++j )
			{
				QDomElement child = children.item( j ).toElement();
				child.setTagName( "freeboy" );
			}
		}
		else if( el.attribute( "name" ) == "OPL2" )
		{
			el.setAttribute( "name", "opulenz" );
			QDomNodeList children = el.elementsByTagName( "OPL2" );
			for( int j = 0; !children.item( j ).isNull(); ++j )
			{
				QDomElement child = children.item( j ).toElement();
				child.setTagName( "opulenz" );
			}
		}
	}

	list = elementsByTagName( "effect" );
	for( int i = 0; !list.item( i ).isNull(); ++i )
	{
		QDomElement effect = list.item( i ).toElement();
		if( effect.attribute( "name" ) == "ladspaeffect" )
		{
			QDomNodeList keys = effect.elementsByTagName( "key" );
			for( int j = 0; !keys.item( j ).isNull(); ++j )
			{
				QDomElement key = keys.item( j ).toElement();
				QDomNodeList attributes = key.elementsByTagName( "attribute" );
				for( int k = 0; !attributes.item( k ).isNull(); ++k )
				{
					// Effect name changes

					QDomElement attribute = attributes.item( k ).toElement();
					if( attribute.attribute( "name" ) == "file" &&
							( attribute.attribute( "value" ) == "calf" ||
							attribute.attribute( "value" ) == "calf.so" ) )
					{
						attribute.setAttribute( "value", "veal" );
					}
					else if( attribute.attribute( "name" ) == "plugin" &&
							attribute.attribute( "value" ) == "Sidechaincompressor" )
					{
						attribute.setAttribute( "value", "SidechainCompressor" );
					}
					else if( attribute.attribute( "name" ) == "plugin" &&
							attribute.attribute( "value" ) == "Sidechaingate" )
					{
						attribute.setAttribute( "value", "SidechainGate" );
					}
					else if( attribute.attribute( "name" ) == "plugin" &&
							attribute.attribute( "value" ) == "Multibandcompressor" )
					{
						attribute.setAttribute( "value", "MultibandCompressor" );
					}
					else if( attribute.attribute( "name" ) == "plugin" &&
							attribute.attribute( "value" ) == "Multibandgate" )
					{
						attribute.setAttribute( "value", "MultibandGate" );
					}
					else if( attribute.attribute( "name" ) == "plugin" &&
							attribute.attribute( "value" ) == "Multibandlimiter" )
					{
						attribute.setAttribute( "value", "MultibandLimiter" );
					}

					// Handle port changes

					if( attribute.attribute( "name" ) == "plugin" &&
							( attribute.attribute( "value" ) == "MultibandLimiter" ||
							attribute.attribute( "value" ) == "MultibandCompressor" ||
							attribute.attribute( "value" ) == "MultibandGate" ) )
					{
						auto fn = [&](QDomElement& port, int num, QList<QDomElement>&, QList<QDomElement>& removeList)
						{
							// Mark ports for removal
							if ( num >= 18 && num <= 23 )
							{
								removeList << port;
							}
							// Bump higher ports up 6 positions
							else if ( num >= 24 )
							{
								// port01...port010, etc
								QString name( "port0" );
								name.append( QString::number( num -6 ) );
								port.setTagName( name );
							}
						};
						iterate_ladspa_ports(effect, fn);
					}

					if( attribute.attribute( "name" ) == "plugin" &&
							( attribute.attribute( "value" ) == "Pulsator" ) )
					{
						auto fn = [&](QDomElement& port, int num, QList<QDomElement>& addList, QList<QDomElement>& removeList)
						{
							switch(num)
							{
								case 16:
								{
									// old freq is now at port 25
									QDomElement portCopy = createElement("port025");
									portCopy.setAttribute("data", port.attribute("data"));
									addList << portCopy;
									// remove old freq port
									removeList << port;
									// set the "timing" port to choose port23+2=port25 (timing in Hz)
									QDomElement timing = createElement("port022");
									timing.setAttribute("data", 2);
									addList << timing;
									break;
								}
								// port 18 (modulation) => 17
								case 17:
									port.setTagName("port016");
									break;
								case 18:
								{
									// leave port 18 (offsetr), but add port 17 (offsetl)
									QDomElement offsetl = createElement("port017");
									offsetl.setAttribute("data", 0.0f);
									addList << offsetl;
									// additional: bash port 21 to 1
									QDomElement pulsewidth = createElement("port021");
									pulsewidth.setAttribute("data", 1.0f);
									addList << pulsewidth;
									break;
								}
							}

						};
						iterate_ladspa_ports(effect, fn);
					}

					if( attribute.attribute( "name" ) == "plugin" &&
							( attribute.attribute( "value" ) == "VintageDelay" ) )
					{
						auto fn = [&](QDomElement& port, int num, QList<QDomElement>& addList, QList<QDomElement>& )
						{
							switch(num)
							{
								case 4:
								{
									// BPM is now port028
									port.setTagName("port028");
									// bash timing to BPM
									QDomElement timing = createElement("port027");
									timing.setAttribute("data", 0);
									addList << timing;

									// port 5 and 6 (in, out gain) need to be bashed to 1:
									QDomElement input = createElement("port05");
									input.setAttribute("data", 1.0f);
									addList << input;
									QDomElement output = createElement("port06");
									output.setAttribute("data", 1.0f);
									addList << output;

									break;
								}
								default:
									// all other ports increase by 10
									QString name( "port0" );
									name.append( QString::number( num + 10 ) );
									port.setTagName( name );
							}

						};
						iterate_ladspa_ports(effect, fn);
					}

					if( attribute.attribute( "name" ) == "plugin" &&
						(	   ( attribute.attribute( "value" ) == "Equalizer5Band" )
							|| ( attribute.attribute( "value" ) == "Equalizer8Band" )
							|| ( attribute.attribute( "value" ) == "Equalizer12Band" ) ) )
					{
						// NBand equalizers got 4 q nobs inserted. We need to shift everything else...
						// HOWEVER: 5 band eq has only 2 q nobs inserted (no LS/HS filters)
						bool band5 = ( attribute.attribute( "value" ) == "Equalizer5Band" );
						auto fn = [&](QDomElement& port, int num, QList<QDomElement>& addList, QList<QDomElement>& )
						{
							if(num == 4)
							{
								// don't modify port 4, but some other ones:
								int zoom_port;
								if(attribute.attribute( "value" ) == "Equalizer5Band")
									zoom_port = 36;
								else if(attribute.attribute( "value" ) == "Equalizer8Band")
									zoom_port = 48;
								else // 12 band
									zoom_port = 64;
								// bash zoom to 0.25
								QString name( "port0" );
								name.append( QString::number( zoom_port ) );
								QDomElement timing = createElement(name);
								timing.setAttribute("data", 0.25f);
								addList << timing;
							}
							// the following code could be refactored, but I did careful code-reading
							// to prevent copy-paste-errors
							if(num == 18)
							{
								// 18 => 19
								port.setTagName("port019");
								// insert port 18 (q)
								QDomElement q = createElement("port018");
								q.setAttribute("data", 0.707f);
								addList << q;
							}
							else if(num >= 19 && num <= 20)
							{
								// num += 1
								QString name( "port0" );
								name.append( QString::number( num + 1 ) );
								port.setTagName( name );
							}
							else if(num == 21)
							{
								// 21 => 23
								port.setTagName("port023");
								// insert port 22 (q)
								QDomElement q = createElement("port022");
								q.setAttribute("data", 0.707f);
								addList << q;
							}
							else if(num >= 22 && (num <= 23 || band5))
							{
								// num += 2
								QString name( "port0" );
								name.append( QString::number( num + 2 ) );
								port.setTagName( name );
							}
							else if(num == 24 && !band5)
							{
								// 24 => 27
								port.setTagName("port027");
								// insert port 26 (q)
								QDomElement q = createElement("port026");
								q.setAttribute("data", 0.707f);
								addList << q;
							}
							else if(num >= 25 && num <= 26 && !band5)
							{
								// num += 3
								QString name( "port0" );
								name.append( QString::number( num + 3 ) );
								port.setTagName( name );
							}
							else if(num == 27 && !band5)
							{
								// 27 => 31
								port.setTagName("port031");
								// insert port 30 (q)
								QDomElement q = createElement("port030");
								q.setAttribute("data", 0.707f);
								addList << q;
							}
							else if(num >= 28 && !band5)
							{
								// num += 4
								QString name( "port0" );
								name.append( QString::number( num + 4 ) );
								port.setTagName( name );
							}
						};
						iterate_ladspa_ports(effect, fn);
					}

					if( attribute.attribute( "name" ) == "plugin" &&
						attribute.attribute( "value" ) == "Saturator" )
					{
						auto fn = [&](QDomElement& port, int num, QList<QDomElement>&, QList<QDomElement>& )
						{
							// These ports have been shifted a bit weird...
							if( num == 7 )
							{
								port.setTagName("port015");
							}
							else if(num == 12)
							{
								port.setTagName("port016");
							}
							else if(num == 13)
							{
								port.setTagName("port017");
							}
							else if ( num >= 15 )
							{
								QString name( "port0" );
								name.append( QString::number( num + 3 ) );
								port.setTagName( name );
							}
						};
						iterate_ladspa_ports(effect, fn);
					}

					if( attribute.attribute( "name" ) == "plugin" &&
						attribute.attribute( "value" ) == "StereoTools" )
					{
						auto fn = [&](QDomElement& port, int num, QList<QDomElement>&, QList<QDomElement>& )
						{
							// This effect can not be back-ported due to bugs in the old version,
							// or due to different behaviour. We thus port all parameters we can,
							// and bash all new parameters (in this case, s.level and m.level) to
							// their new defaults (both 1.0f in this case)

							if( num == 23 || num == 25 )
							{
								port.setAttribute("data", 1.0f);
							}
						};
						iterate_ladspa_ports(effect, fn);
					}
				}
			}
		}
	}
}

void DataFile::upgrade_noHiddenClipNames()
{
	QDomNodeList tracks = elementsByTagName("track");

	auto clearDefaultNames = [](QDomNodeList clips, QString trackName)
	{
		for (int j = 0; j < clips.size(); ++j)
		{
			QDomElement clip = clips.item(j).toElement();
			QString clipName = clip.attribute("name", "");
			if (clipName == trackName) { clip.setAttribute("name", ""); }
		}
	};

	for (int i = 0; i < tracks.size(); ++i)
	{
		QDomElement track = tracks.item(i).toElement();
		QString trackName = track.attribute("name", "");

		QDomNodeList instClips = track.elementsByTagName("pattern");
		QDomNodeList autoClips = track.elementsByTagName("automationpattern");
		QDomNodeList bbClips = track.elementsByTagName("bbtco");

		clearDefaultNames(instClips, trackName);
		clearDefaultNames(autoClips, trackName);
		clearDefaultNames(bbClips, trackName);
	}
}

void DataFile::upgrade()
{
	// Runs all necessary upgrade methods
	std::for_each( UPGRADE_METHODS.begin() + m_fileVersion, UPGRADE_METHODS.end(),
		[this](UpgradeMethod um)
		{
			(this->*um)();
		}
	);

	// Bump the file version (which should be the size of the upgrade methods vector)
	m_fileVersion = UPGRADE_METHODS.size();

	// update document meta data
	documentElement().setAttribute( "version", m_fileVersion );
	documentElement().setAttribute( "type", typeName( type() ) );
	documentElement().setAttribute( "creator", "LMMS" );
	documentElement().setAttribute( "creatorversion", LMMS_VERSION );

	if( type() == SongProject || type() == SongProjectTemplate )
	{
		// Time-signature
		if ( !m_head.hasAttribute( "timesig_numerator" ) )
		{
			m_head.setAttribute( "timesig_numerator", 4 );
			m_head.setAttribute( "timesig_denominator", 4 );
		}

		if( !m_head.hasAttribute( "mastervol" ) )
		{
			m_head.setAttribute( "mastervol", 100 );
		}
	}
}

void DataFile::loadData( const QByteArray & _data, const QString & _sourceFile )
{
	QString errorMsg;
	int line = -1, col = -1;
	if( !setContent( _data, &errorMsg, &line, &col ) )
	{
		// parsing failed? then try to uncompress data
		QByteArray uncompressed = qUncompress( _data );
		if( !uncompressed.isEmpty() )
		{
			if( setContent( uncompressed, &errorMsg, &line, &col ) )
			{
				line = col = -1;
			}
		}
		if( line >= 0 && col >= 0 )
		{
			qWarning() << "at line" << line << "column" << errorMsg;
			if( gui )
			{
				QMessageBox::critical( NULL,
					SongEditor::tr( "Error in file" ),
					SongEditor::tr( "The file %1 seems to contain "
							"errors and therefore can't be "
							"loaded." ).
								arg( _sourceFile ) );
			}

			return;
		}
	}

	QDomElement root = documentElement();
	m_type = type( root.attribute( "type" ) );
	m_head = root.elementsByTagName( "head" ).item( 0 ).toElement();

	if (!root.hasAttribute("version") || root.attribute("version")=="1.0")
	{
		// The file versioning is now a unsigned int, not maj.min, so we use
		// legacyFileVersion() to retrieve the appropriate version
		m_fileVersion = legacyFileVersion();
	}
	else
	{
		bool success;
		m_fileVersion = root.attribute( "version" ).toUInt( &success );
		if( !success ) qWarning("File Version conversion failure.");
	}

	if (root.hasAttribute("creatorversion"))
	{
		// compareType defaults to All, so it doesn't have to be set here
		ProjectVersion createdWith = root.attribute("creatorversion");
		ProjectVersion openedWith = LMMS_VERSION;

		if (createdWith < openedWith) { upgrade(); }

		if (createdWith.setCompareType(ProjectVersion::Minor)
		 !=  openedWith.setCompareType(ProjectVersion::Minor)
		 && gui != nullptr && root.attribute("type") == "song"
		){
			auto projectType = _sourceFile.endsWith(".mpt") ?
				SongEditor::tr("template") : SongEditor::tr("project");

			TextFloat::displayMessage(
				SongEditor::tr("Version difference"),
				SongEditor::tr("This %1 was created with LMMS %2")
				.arg(projectType).arg(createdWith.getVersion()),
				embed::getIconPixmap("whatsthis", 24, 24),
				2500
			);
		}
	}

	m_content = root.elementsByTagName(typeName(m_type)).item(0).toElement();
}

void findIds(const QDomElement& elem, QList<jo_id_t>& idList)
{
	if(elem.hasAttribute("id"))
	{
		idList.append(elem.attribute("id").toInt());
	}
	QDomElement child = elem.firstChildElement();
	while(!child.isNull())
	{
		findIds(child, idList);
		child = child.nextSiblingElement();
	}
}

unsigned int DataFile::legacyFileVersion()
{
	// Version of LMMs that created this project
	ProjectVersion creator =
		documentElement().attribute( "creatorversion" ).
		replace( "svn", "" );

	// Get an iterator pointing at the first upgrade we need to run (or at the end if there is no such upgrade)
	auto firstRequiredUpgrade = std::upper_bound( UPGRADE_VERSIONS.begin(), UPGRADE_VERSIONS.end(), creator );

	// Convert the iterator to an index, which is our file version (starting at 0)
	return std::distance( UPGRADE_VERSIONS.begin(), firstRequiredUpgrade );
}


code:
/*
 * DrumSynth.cpp - DrumSynth DS file renderer
 *
 * Copyright (c) 1998-2000 Paul Kellett (mda-vst.com)
 * Copyright (c) 2007 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 "DrumSynth.h"

#include <sstream>
#include <cstring>

#include <math.h>     //sin(), exp(), etc.

#include <QFile>

#ifdef LMMS_BUILD_WIN32
#define powf pow
#endif

#ifdef _MSC_VER
//not #if LMMS_BUILD_WIN32 because we have strncasecmp in mingw
#define strcasecmp _stricmp
#endif

using namespace std;

#define WORD  __u16
#define DWORD __u32
#define WAVE_FORMAT_PCM			0x0001

// const int     Fs    =  44100;
const float   TwoPi =  6.2831853f;
const int     MAX   =  0;
const int     ENV   =  1;
const int     PNT   =  2;
const int     dENV  =  3;
const int     NEXTT =  4;

// Bah, I'll move these into the class once I sepearate DrumsynthFile from DrumSynth
// llama
float envpts[8][3][32];    //envelope/time-level/point
float envData[8][6];       //envelope running status
int   chkOn[8], sliLev[8]; //section on/off and level
float timestretch;         //overall time scaling
short DD[1200], clippoint;
float DF[1200];
float phi[1200];

long  wavewords, wavemode=0;
float mem_t=1.0f, mem_o=1.0f, mem_n=1.0f, mem_b=1.0f, mem_tune=1.0f, mem_time=1.0f;

int DrumSynth::LongestEnv(void)
{
  long e, eon, p;
  float l=0.f;

  for(e=1; e<7; e++) //3
  {
    eon = e - 1; if(eon>2) eon=eon-1;
    p = 0;
    while (envpts[e][0][p + 1] >= 0.f) p++;
    envData[e][MAX] = envpts[e][0][p] * timestretch;
    if(chkOn[eon]==1) if(envData[e][MAX]>l) l=envData[e][MAX];
  }
  //l *= timestretch;

  return 2400 + (1200 * (int)(l / 1200));
}

float DrumSynth::LoudestEnv(void)
{
  float loudest=0.f;
  int i=0;

  while (i<5) //2
  {
    if(chkOn[i]==1) if(sliLev[i]>loudest) loudest=(float)sliLev[i];
    i++;
  }
  return (loudest * loudest);
}

void DrumSynth::UpdateEnv(int e, long t)
{
  float endEnv, dT;
                                                             //0.2's added
  envData[e][NEXTT] = envpts[e][0][(long)(envData[e][PNT] + 1.f)] * timestretch; //get next point
  if(envData[e][NEXTT] < 0) envData[e][NEXTT] = 442000 * timestretch; //if end point, hold
  envData[e][ENV] = envpts[e][1][(long)(envData[e][PNT] + 0.f)] * 0.01f; //this level
  endEnv = envpts[e][1][(long)(envData[e][PNT] + 1.f)] * 0.01f;          //next level
  dT = envData[e][NEXTT] - (float)t;
  if(dT < 1.0) dT = 1.0;
  envData[e][dENV] = (endEnv - envData[e][ENV]) / dT;
  envData[e][PNT] = envData[e][PNT] + 1.0f;
}

void DrumSynth::GetEnv(int env, const char *sec, const char *key, QString ini)
{
  char en[256], s[8];
  int i=0, o=0, ep=0;
  GetPrivateProfileString(sec, key, "0,0 100,0", en, sizeof(en), ini);

  //be safe!
  en[255]=0;
  s[0]=0;

  while(en[i]!=0)
  {
    if(en[i] == ',')
    {
      if(sscanf(s, "%f", &envpts[env][0][ep])==0) envpts[env][0][ep] = 0.f;
      o=0;
    }
    else if(en[i] == ' ')
    {
      if(sscanf(s, "%f", &envpts[env][1][ep])==0) envpts[env][1][ep] = 0.f;
      o=0; ep++;
    }
    else { s[o]=en[i]; o++; s[o]=0; }
    i++;
  }
  if(sscanf(s, "%f", &envpts[env][1][ep])==0) envpts[env][1][ep] = 0.f;
  envpts[env][0][ep + 1] = -1;

  envData[env][MAX] = envpts[env][0][ep];
}

float DrumSynth::waveform(float ph, int form)
{
  float w;

  switch (form)
  {
     case 0: w = (float)sin(fmod(ph,TwoPi));                            break; //sine
     case 1: w = (float)fabs(2.0f*(float)sin(fmod(0.5f*ph,TwoPi)))-1.f; break; //sine^2
     case 2: while(ph<TwoPi) ph+=TwoPi;
             w = 0.6366197f * (float)fmod(ph,TwoPi) - 1.f;                     //tri
             if(w>1.f) w=2.f-w;
             break;
     case 3: w = ph - TwoPi * (float)(int)(ph / TwoPi);                        //saw
             w = (0.3183098f * w) - 1.f;                                break;
    default: w = (sin(fmod(ph,TwoPi))>0.0)? 1.f: -1.f;                  break; //square
  }

  return w;
}

int DrumSynth::GetPrivateProfileString(const char *sec, const char *key, const char *def, char *buffer, int size, QString file)
{
    stringstream is;
    bool inSection = false;
    char *line;
    char *k, *b;
    int len = 0;

    line = (char*)malloc(200);

    // Use QFile to handle unicode file name on Windows
    // Previously we used ifstream directly
    QFile f(file);
    f.open(QIODevice::ReadOnly);
    QByteArray dat = f.readAll().constData();
    is.str(string(dat.constData(), dat.size()));

    while (is.good()) {
        if (!inSection) {
            is.ignore( numeric_limits<streamsize>::max(), '[');

            if (!is.eof()) {
                is.getline(line, 200, ']');
                if (strcasecmp(line, sec)==0) {
                    inSection = true;
                }
            }
        }
        else if (!is.eof()) {
            is.getline(line, 200);
            if (line[0] == '[')
                break;

            k = strtok(line, " \t=");
            b = strtok(NULL, "\n\r\0");

            if (k != 0 && strcasecmp(k, key)==0) {
                if (b==0) {
                    len = 0;
                    buffer[0] = 0;
                }
                else {
                    k = (char *)(b + strlen(b)-1);
                    while ( (k>=b) && (*k==' ' || *k=='\t') )
                        --k;
                    *(k+1) = '\0';

                    len = strlen(b);
                    if (len > size-1) len = size-1;
                    strncpy(buffer, b, len+1);
                }
                break;
            }
        }
    }

    if (len == 0) {
        len = strlen(def);
        strncpy(buffer, def, size);
    }

    free(line);

    return len;
}

int DrumSynth::GetPrivateProfileInt(const char *sec, const char *key, int def, QString file)
{
  char tmp[16];
  int i=0;

  GetPrivateProfileString(sec, key, "", tmp, sizeof(tmp), file);
  sscanf(tmp, "%d", &i); if(tmp[0]==0) i=def;

  return i;
}

float DrumSynth::GetPrivateProfileFloat(const char *sec, const char *key, float def, QString file)
{
    char tmp[16];
    float f=0.f;

    GetPrivateProfileString(sec, key, "", tmp, sizeof(tmp), file);
    sscanf(tmp, "%f", &f); if(tmp[0]==0) f=def;

    return f;
}

//  Constantly opening and scanning each file for the setting really sucks.
//  But, the original did this, and we know it works.  Will store file into
//  an associative array or something once we have a datastructure to load in to.
//  llama

int DrumSynth::GetDSFileSamples(QString dsfile, int16_t *&wave, int channels, sample_rate_t Fs)
{
  //input file
  char sec[32];
  char ver[32];
  char comment[256];
  int commentLen=0;

  //generation
  long  Length, tpos=0, tplus, totmp, t, i, j;
  float x[3] = {0.f, 0.f, 0.f};
  float MasterTune, randmax, randmax2;
  int   MainFilter, HighPass;

  long  NON, NT, TON, DiON, TDroop=0, DStep;
  float a, b=0.f, c=0.f, d=0.f, g, TT=0.f, TL, NL, F1, F2;
  float TphiStart=0.f, Tphi, TDroopRate, ddF, DAtten, DGain;

  long  BON, BON2, BFStep, BFStep2, botmp;
  float BdF=0.f, BdF2=0.f, BPhi, BPhi2, BF, BF2, BQ, BQ2, BL, BL2;

  long  OON, OF1Sync=0, OF2Sync=0, OMode, OW1, OW2;
  float Ophi1, Ophi2, OF1, OF2, OL, Ot=0 /*PG: init */, OBal1, OBal2, ODrive;
  float Ocf1, Ocf2, OcF, OcQ, OcA, Oc[6][2];  //overtone cymbal mode
  float Oc0=0.0f, Oc1=0.0f, Oc2=0.0f;

  float MFfb, MFtmp, MFres, MFin=0.f, MFout=0.f;
  float DownAve;
  long  DownStart, DownEnd, jj;

  if(wavemode==0) //semi-real-time adjustments if working in memory!!
  {
    mem_t = 1.0f;
    mem_o = 1.0f;
    mem_n = 1.0f;
    mem_b = 1.0f;
    mem_tune = 0.0f;
    mem_time = 1.0f;
  }

  //try to read version from input file
  strcpy(sec, "General");
  GetPrivateProfileString(sec,"Version","",ver,sizeof(ver),dsfile);
  ver[9]=0;
  if(strcasecmp(ver, "DrumSynth") != 0) {return 0;} //input fail
  if(ver[11] != '1' && ver[11] != '2') {return 0;} //version fail

  //read master parameters
  GetPrivateProfileString(sec,"Comment","",comment,sizeof(comment),dsfile);
  while((comment[commentLen]!=0) && (commentLen<254)) commentLen++;
  if(commentLen==0) { comment[0]=32; comment[1]=0; commentLen=1;}
  comment[commentLen+1]=0; commentLen++;
  if((commentLen % 2)==1) commentLen++;

  timestretch = .01f * mem_time * GetPrivateProfileFloat(sec,"Stretch",100.0,dsfile);
  if(timestretch<0.2f) timestretch=0.2f;
  if(timestretch>10.f) timestretch=10.f;
  // the unit of envelope lengths is a sample in 44100Hz sample rate, so correct it
  timestretch *= Fs / 44100.f;

  DGain = 1.0f; //leave this here!
  DGain = (float)powf(10.0, 0.05 * GetPrivateProfileFloat(sec,"Level",0,dsfile));

  MasterTune = GetPrivateProfileFloat(sec,"Tuning",0.0,dsfile);
  MasterTune = (float)powf(1.0594631f, MasterTune + mem_tune);
  MainFilter = 2 * GetPrivateProfileInt(sec,"Filter",0,dsfile);
  MFres = 0.0101f * GetPrivateProfileFloat(sec,"Resonance",0.0,dsfile);
  MFres = (float)powf(MFres, 0.5f);

  HighPass = GetPrivateProfileInt(sec,"HighPass",0,dsfile);
  GetEnv(7, sec, "FilterEnv", dsfile);

  //read noise parameters
  strcpy(sec, "Noise");
  chkOn[1] = GetPrivateProfileInt(sec,"On",0,dsfile);
  sliLev[1] = GetPrivateProfileInt(sec,"Level",0,dsfile);
  NT =  GetPrivateProfileInt(sec,"Slope",0,dsfile);
  GetEnv(2, sec, "Envelope", dsfile);
  NON = chkOn[1];
  NL = (float)(sliLev[1] * sliLev[1]) * mem_n;
  if(NT<0)
  { a = 1.f + (NT / 105.f); d = -NT / 105.f;
    g = (1.f + 0.0005f * NT * NT) * NL; }
  else
  { a = 1.f; b = -NT / 50.f; c = (float)fabs((float)NT) / 100.f; g = NL; }

  //if(GetPrivateProfileInt(sec,"FixedSeq",0,dsfile)!=0)
    //srand(1); //fixed random sequence

   //read tone parameters
  strcpy(sec, "Tone");
  chkOn[0] = GetPrivateProfileInt(sec,"On",0,dsfile); TON = chkOn[0];
  sliLev[0] = GetPrivateProfileInt(sec,"Level",128,dsfile);
  TL = (float)(sliLev[0] * sliLev[0]) * mem_t;
  GetEnv(1, sec, "Envelope", dsfile);
  F1 = MasterTune * TwoPi * GetPrivateProfileFloat(sec,"F1",200.0,dsfile) / Fs;
  if(fabs(F1)<0.001f) F1=0.001f; //to prevent overtone ratio div0
  F2 = MasterTune * TwoPi * GetPrivateProfileFloat(sec,"F2",120.0,dsfile) / Fs;
  TDroopRate = GetPrivateProfileFloat(sec,"Droop",0.f,dsfile);
  if(TDroopRate>0.f)
  {
    TDroopRate = (float)powf(10.0f, (TDroopRate - 20.0f) / 30.0f);
    TDroopRate = TDroopRate * -4.f / envData[1][MAX];
    TDroop = 1;
    F2 = F1+((F2-F1)/(1.f-(float)exp(TDroopRate * envData[1][MAX])));
    ddF = F1 - F2;
  }
  else ddF = F2-F1;

  Tphi = GetPrivateProfileFloat(sec,"Phase",90.f,dsfile) / 57.29578f; //degrees>radians

  //read overtone parameters
  strcpy(sec, "Overtones");
  chkOn[2] = GetPrivateProfileInt(sec,"On",0,dsfile); OON = chkOn[2];
  sliLev[2] = GetPrivateProfileInt(sec,"Level",128,dsfile);
  OL = (float)(sliLev[2] * sliLev[2]) * mem_o;
  GetEnv(3, sec, "Envelope1", dsfile);
  GetEnv(4, sec, "Envelope2", dsfile);
  OMode = GetPrivateProfileInt(sec,"Method",2,dsfile);
  OF1 = MasterTune * TwoPi * GetPrivateProfileFloat(sec,"F1",200.0,dsfile) / Fs;
  OF2 = MasterTune * TwoPi * GetPrivateProfileFloat(sec,"F2",120.0,dsfile) / Fs;
  OW1 = GetPrivateProfileInt(sec,"Wave1",0,dsfile);
  OW2 = GetPrivateProfileInt(sec,"Wave2",0,dsfile);
  OBal2 = (float)GetPrivateProfileInt(sec,"Param",50,dsfile);
  ODrive = (float)powf(OBal2, 3.0f) / (float)powf(50.0f, 3.0f);
  OBal2 *= 0.01f;
  OBal1 = 1.f - OBal2;
  Ophi1 = Tphi;
  Ophi2 = Tphi;
  if(MainFilter==0)
    MainFilter = GetPrivateProfileInt(sec,"Filter",0,dsfile);
  if((GetPrivateProfileInt(sec,"Track1",0,dsfile)==1) && (TON==1))
  { OF1Sync = 1;  OF1 = OF1 / F1; }
  if((GetPrivateProfileInt(sec,"Track2",0,dsfile)==1) && (TON==1))
  { OF2Sync = 1;  OF2 = OF2 / F1; }

  OcA = 0.28f + OBal1 * OBal1;  //overtone cymbal mode
  OcQ = OcA * OcA;
  OcF = (1.8f - 0.7f * OcQ) * 0.92f; //multiply by env 2
  OcA *= 1.0f + 4.0f * OBal1;  //level is a compromise!
  Ocf1 = TwoPi / OF1;
  Ocf2 = TwoPi / OF2;
  for(i=0; i<6; i++) Oc[i][0] = Oc[i][1] = Ocf1 + (Ocf2 - Ocf1) * 0.2f * (float)i;

  //read noise band parameters
  strcpy(sec, "NoiseBand");
  chkOn[3] = GetPrivateProfileInt(sec,"On",0,dsfile); BON = chkOn[3];
  sliLev[3] = GetPrivateProfileInt(sec,"Level",128,dsfile);
  BL = (float)(sliLev[3] * sliLev[3]) * mem_b;
  BF = MasterTune * TwoPi * GetPrivateProfileFloat(sec,"F",1000.0,dsfile) / Fs;
  BPhi = TwoPi / 8.f;
  GetEnv(5, sec, "Envelope", dsfile);
  BFStep = GetPrivateProfileInt(sec,"dF",50,dsfile);
  BQ = (float)BFStep;
  BQ = BQ * BQ / (10000.f-6600.f*((float)sqrt(BF)-0.19f));
  BFStep = 1 + (int)((40.f - (BFStep / 2.5f)) / (BQ + 1.f + (1.f * BF)));

  strcpy(sec, "NoiseBand2");
  chkOn[4] = GetPrivateProfileInt(sec,"On",0,dsfile); BON2 = chkOn[4];
  sliLev[4] = GetPrivateProfileInt(sec,"Level",128,dsfile);
  BL2 = (float)(sliLev[4] * sliLev[4]) * mem_b;
  BF2 = MasterTune * TwoPi * GetPrivateProfileFloat(sec,"F",1000.0,dsfile) / Fs;
  BPhi2 = TwoPi / 8.f;
  GetEnv(6, sec, "Envelope", dsfile);
  BFStep2 = GetPrivateProfileInt(sec,"dF",50,dsfile);
  BQ2 = (float)BFStep2;
  BQ2 = BQ2 * BQ2 / (10000.f-6600.f*((float)sqrt(BF2)-0.19f));
  BFStep2 = 1 + (int)((40 - (BFStep2 / 2.5)) / (BQ2 + 1 + (1 * BF2)));

  //read distortion parameters
  strcpy(sec, "Distortion");
  chkOn[5] = GetPrivateProfileInt(sec,"On",0,dsfile); DiON = chkOn[5];
  DStep = 1 + GetPrivateProfileInt(sec,"Rate",0,dsfile);
  if(DStep==7) DStep=20;
  if(DStep==6) DStep=10;
  if(DStep==5) DStep=8;

  clippoint = 32700;
  DAtten = 1.0f;

  if(DiON==1)
  {
    DAtten = DGain * (short)LoudestEnv();
    if(DAtten>32700) clippoint=32700; else clippoint=(short)DAtten;
    DAtten = (float)powf(2.0, 2.0 * GetPrivateProfileInt(sec,"Bits",0,dsfile));
    DGain = DAtten * DGain * (float)powf(10.0, 0.05 * GetPrivateProfileInt(sec,"Clipping",0,dsfile));
  }

  //prepare envelopes
  randmax = 1.f / RAND_MAX; randmax2 = 2.f * randmax;
  for (i=1;i<8;i++) { envData[i][NEXTT]=0; envData[i][PNT]=0; }
  Length = LongestEnv();

  //allocate the buffer
  //if(wave!=NULL) free(wave);
  //wave = new int16_t[channels * (Length + 1280)]; //wave memory buffer
  wave = new int16_t[channels * Length]; //wave memory buffer
  if(wave==NULL) {return 0;}
  wavewords = 0;

  /*
  if(wavemode==0)
  {
    //open output file
    fp = fopen(wavfile, "wb");
    if(!fp) {return 3;} //output fail

     //set up INFO chunk
    WI.list = 0x5453494C;
    WI.listLength = 36 + commentLen;
    WI.info = 0x4F464E49;
    WI.isft = 0x54465349;
    WI.isftLength = 16;
    strcpy(WI.software, "DrumSynth v2.0 "); WI.software[15]=0;
    WI.icmt = 0x544D4349;
    WI.icmtLength = commentLen;

    //write WAV header
    WH.riff = 0x46464952;
    WH.riffLength = 36 + (2 * Length) + 44 + commentLen;
    WH.wave = 0x45564157;
    WH.fmt = 0x20746D66;
    WH.waveLength = 16;
    WH.wFormatTag = WAVE_FORMAT_PCM;
    WH.nChannels = 1;
    WH.nSamplesPerSec = Fs;
    WH.nAvgBytesPerSec = 2 * Fs;
    WH.nBlockAlign = 2;
    WH.wBitsPerSample = 16;
    WH.data = 0x61746164;
    WH.dataLength = 2 * Length;
    fwrite(&WH, 1, 44, fp);
  }
  */

  //generate
  tpos = 0;
  while(tpos<Length)
  {
    tplus = tpos + 1199;

    if(NON==1) //noise
    {
      for(t=tpos; t<=tplus; t++)
      {
        if(t < envData[2][NEXTT]) envData[2][ENV] = envData[2][ENV] + envData[2][dENV];
        else UpdateEnv(2, t);
        x[2] = x[1];
        x[1] = x[0];
        x[0] = (randmax2 * (float)rand()) - 1.f;
        TT = a * x[0] + b * x[1] + c * x[2] + d * TT;
        DF[t - tpos] = TT * g * envData[2][ENV];
      }
      if(t>=envData[2][MAX]) NON=0;
    }
    else {
        for(j=0; j<1200; j++) DF[j]=0.f;
    }

    if(TON==1) //tone
    {
      TphiStart = Tphi;
      if(TDroop==1)
      {
        for(t=tpos; t<=tplus; t++)
          phi[t - tpos] = F2 + (ddF * (float)exp(t * TDroopRate));
      }
      else
      {
        for(t=tpos; t<=tplus; t++)
          phi[t - tpos] = F1 + (t / envData[1][MAX]) * ddF;
      }
      for(t=tpos; t<=tplus; t++)
      {
        totmp = t - tpos;
        if(t < envData[1][NEXTT])
          envData[1][ENV] = envData[1][ENV] + envData[1][dENV];
        else UpdateEnv(1, t);
        Tphi = Tphi + phi[totmp];
        DF[totmp] += TL * envData[1][ENV] * (float)sin(fmod(Tphi,TwoPi));//overflow?
      }
      if(t>=envData[1][MAX]) TON=0;
    }
    else for(j=0; j<1200; j++) phi[j]=F2; //for overtone sync

    if(BON==1) //noise band 1
    {
      for(t=tpos; t<=tplus; t++)
      {
        if(t < envData[5][NEXTT])
          envData[5][ENV] = envData[5][ENV] + envData[5][dENV];
        else UpdateEnv(5, t);
        if((t % BFStep) == 0) BdF = randmax * (float)rand() - 0.5f;
        BPhi = BPhi + BF + BQ * BdF;
        botmp = t - tpos;
        DF[botmp] = DF[botmp] + (float)cos(fmod(BPhi,TwoPi)) * envData[5][ENV] * BL;
      }
      if(t>=envData[5][MAX]) BON=0;
    }

    if(BON2==1) //noise band 2
    {
      for(t=tpos; t<=tplus; t++)
      {
        if(t < envData[6][NEXTT])
          envData[6][ENV] = envData[6][ENV] + envData[6][dENV];
        else UpdateEnv(6, t);
        if((t % BFStep2) == 0) BdF2 = randmax * (float)rand() - 0.5f;
        BPhi2 = BPhi2 + BF2 + BQ2 * BdF2;
        botmp = t - tpos;
        DF[botmp] = DF[botmp] + (float)cos(fmod(BPhi2,TwoPi)) * envData[6][ENV] * BL2;
      }
      if(t>=envData[6][MAX]) BON2=0;
    }

    for (t=tpos; t<=tplus; t++)
    {
      if(OON==1) //overtones
      {
        if(t<envData[3][NEXTT])
          envData[3][ENV] = envData[3][ENV] + envData[3][dENV];
        else
        {
          if(t>=envData[3][MAX]) //wait for OT2
          {
            envData[3][ENV] = 0;
            envData[3][dENV] = 0;
            envData[3][NEXTT] = 999999;
          }
          else UpdateEnv(3, t);
        }
        //
        if(t<envData[4][NEXTT])
          envData[4][ENV] = envData[4][ENV] + envData[4][dENV];
        else
        {
          if(t>=envData[4][MAX]) //wait for OT1
          {
            envData[4][ENV] = 0;
            envData[4][dENV] = 0;
            envData[4][NEXTT] = 999999;
          }
          else UpdateEnv(4, t);
        }
        //
        TphiStart = TphiStart + phi[t - tpos];
        if(OF1Sync==1) Ophi1 = TphiStart * OF1; else Ophi1 = Ophi1 + OF1;
        if(OF2Sync==1) Ophi2 = TphiStart * OF2; else Ophi2 = Ophi2 + OF2;
        Ot=0.0f;
        switch (OMode)
        {
          case 0: //add
            Ot = OBal1 * envData[3][ENV] * waveform(Ophi1, OW1);
            Ot = OL * (Ot + OBal2 * envData[4][ENV] * waveform(Ophi2, OW2));
            break;

          case 1: //FM
            Ot = ODrive * envData[4][ENV] * waveform(Ophi2, OW2);
            Ot = OL * envData[3][ENV] * waveform(Ophi1 + Ot, OW1);
            break;

          case 2: //RM
            Ot = (1 - ODrive / 8) + (((ODrive / 8) * envData[4][ENV]) * waveform(Ophi2, OW2));
            Ot = OL * envData[3][ENV] * waveform(Ophi1, OW1) * Ot;
            break;

          case 3: //808 Cymbal
            for(j=0; j<6; j++)
            {
              Oc[j][0] += 1.0f;

              if(Oc[j][0]>Oc[j][1])
              {
                Oc[j][0] -= Oc[j][1];
                Ot = OL * envData[3][ENV];
              }
            }
            Ocf1 = envData[4][ENV] * OcF;  //filter freq
            Oc0 += Ocf1 * Oc1;
            Oc1 += Ocf1 * (Ot + Oc2 - OcQ * Oc1 - Oc0);  //bpf
            Oc2 = Ot;
            Ot = Oc1;
            break;
        }
      }

      if(MainFilter==1) //filter overtones
      {
        if(t<envData[7][NEXTT])
          envData[7][ENV] = envData[7][ENV] + envData[7][dENV];
        else UpdateEnv(7, t);

        MFtmp = envData[7][ENV];
        if(MFtmp >0.2f)
          MFfb = 1.001f - (float)powf(10.0f, MFtmp - 1);
        else
          MFfb = 0.999f - 0.7824f * MFtmp;

        MFtmp = Ot + MFres * (1.f + (1.f/MFfb)) * (MFin - MFout);
        MFin = MFfb * (MFin - MFtmp) + MFtmp;
        MFout = MFfb * (MFout - MFin) + MFin;

        DF[t - tpos] = DF[t - tpos] + (MFout - (HighPass * Ot));
      }
      else if(MainFilter==2) //filter all
      {
        if(t<envData[7][NEXTT])
          envData[7][ENV] = envData[7][ENV] + envData[7][dENV];
        else UpdateEnv(7, t);

        MFtmp = envData[7][ENV];
        if(MFtmp >0.2f)
          MFfb = 1.001f - (float)powf(10.0f, MFtmp - 1);
        else
          MFfb = 0.999f - 0.7824f * MFtmp;

        MFtmp = DF[t - tpos] + Ot + MFres * (1.f + (1.f/MFfb)) * (MFin - MFout);
        MFin = MFfb * (MFin - MFtmp) + MFtmp;
        MFout = MFfb * (MFout - MFin) + MFin;

        DF[t - tpos] = MFout - (HighPass * (DF[t - tpos] + Ot));
      }
      // PG: Ot is uninitialized
      else DF[t - tpos] = DF[t - tpos] + Ot; //no filter
    }

    if(DiON==1) //bit resolution
    {
      for(j=0; j<1200; j++)
        DF[j] = DGain * (int)(DF[j] / DAtten);

      for(j=0; j<1200; j+=DStep) //downsampling
      {
        DownAve = 0;
        DownStart = j;
        DownEnd = j + DStep - 1;
        for(jj = DownStart; jj<=DownEnd; jj++)
          DownAve = DownAve + DF[jj];
        DownAve = DownAve / DStep;
        for(jj = DownStart; jj<=DownEnd; jj++)
          DF[jj] = DownAve;
      }
    }
    else for(j=0; j<1200; j++) DF[j] *= DGain;

    for(j = 0; j<1200; j++) //clipping + output
    {
      if(DF[j] > clippoint)
        wave[wavewords++] = clippoint;
      else if(DF[j] < -clippoint)
          wave[wavewords++] = -clippoint;
      else
          wave[wavewords++] = (short)DF[j];

      for (int c = 1; c < channels; c++)  {
        wave[wavewords] = wave[wavewords-1];
        wavewords++;
      }
    }

    tpos = tpos + 1200;
  }

  /*
  if(wavemode==0)
  {
    fwrite(wave, 2, Length, fp);  //write data
    fwrite(&WI,  1, 44, fp); //write INFO chunk
    fwrite(&comment, 1, commentLen, fp);
    fclose(fp);
  }
  wavemode = 0; //force compatibility!!
  */

  return Length;
}


code:
/*
 * Effect.cpp - base-class for effects
 *
 * Copyright (c) 2006-2007 Danny McRae <khjklujn/at/users.sourceforge.net>
 * 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 "Effect.h"
#include "EffectChain.h"
#include "EffectControls.h"
#include "EffectView.h"

#include "ConfigManager.h"

Effect::Effect( const Plugin::Descriptor * _desc,
			Model * _parent,
			const Descriptor::SubPluginFeatures::Key * _key ) :
	Plugin( _desc, _parent, _key ),
	m_parent( NULL ),
	m_processors( 1 ),
	m_okay( true ),
	m_noRun( false ),
	m_running( false ),
	m_bufferCount( 0 ),
	m_enabledModel( true, this, tr( "Effect enabled" ) ),
	m_wetDryModel( 1.0f, -1.0f, 1.0f, 0.01f, this, tr( "Wet/Dry mix" ) ),
	m_gateModel( 0.0f, 0.0f, 1.0f, 0.01f, this, tr( "Gate" ) ),
	m_autoQuitModel( 1.0f, 1.0f, 8000.0f, 100.0f, 1.0f, this, tr( "Decay" ) ),
	m_autoQuitDisabled( false )
{
	m_srcState[0] = m_srcState[1] = NULL;
	reinitSRC();

	if( ConfigManager::inst()->value( "ui", "disableautoquit").toInt() )
	{
		m_autoQuitDisabled = true;
	}
}

Effect::~Effect()
{
	for( int i = 0; i < 2; ++i )
	{
		if( m_srcState[i] != NULL )
		{
			src_delete( m_srcState[i] );
		}
	}
}

void Effect::saveSettings( QDomDocument & _doc, QDomElement & _this )
{
	m_enabledModel.saveSettings( _doc, _this, "on" );
	m_wetDryModel.saveSettings( _doc, _this, "wet" );
	m_autoQuitModel.saveSettings( _doc, _this, "autoquit" );
	m_gateModel.saveSettings( _doc, _this, "gate" );
	controls()->saveState( _doc, _this );
}

void Effect::loadSettings( const QDomElement & _this )
{
	m_enabledModel.loadSettings( _this, "on" );
	m_wetDryModel.loadSettings( _this, "wet" );
	m_autoQuitModel.loadSettings( _this, "autoquit" );
	m_gateModel.loadSettings( _this, "gate" );

	QDomNode node = _this.firstChild();
	while( !node.isNull() )
	{
		if( node.isElement() )
		{
			if( controls()->nodeName() == node.nodeName() )
			{
				controls()->restoreState( node.toElement() );
			}
		}
		node = node.nextSibling();
	}
}

Effect * Effect::instantiate( const QString& pluginName,
				Model * _parent,
				Descriptor::SubPluginFeatures::Key * _key )
{
	Plugin * p = Plugin::instantiateWithKey( pluginName, _parent, _key );
	// check whether instantiated plugin is an effect
	if( dynamic_cast<Effect *>( p ) != NULL )
	{
		// everything ok, so return pointer
		Effect * effect = dynamic_cast<Effect *>( p );
		effect->m_parent = dynamic_cast<EffectChain *>(_parent);
		return effect;
	}

	// not quite... so delete plugin and leave it up to the caller to instantiate a DummyEffect
	delete p;

	return NULL;
}

void Effect::checkGate( double _out_sum )
{
	if( m_autoQuitDisabled )
	{
		return;
	}

	// Check whether we need to continue processing input.  Restart the
	// counter if the threshold has been exceeded.
	if( _out_sum - gate() <= typeInfo<float>::minEps() )
	{
		incrementBufferCount();
		if( bufferCount() > timeout() )
		{
			stopRunning();
			resetBufferCount();
		}
	}
	else
	{
		resetBufferCount();
	}
}

PluginView * Effect::instantiateView( QWidget * _parent )
{
	return new EffectView( this, _parent );
}

void Effect::reinitSRC()
{
	for( int i = 0; i < 2; ++i )
	{
		if( m_srcState[i] != NULL )
		{
			src_delete( m_srcState[i] );
		}
		int error;
		if( ( m_srcState[i] = src_new(
			Engine::mixer()->currentQualitySettings().
							libsrcInterpolation(),
					DEFAULT_CHANNELS, &error ) ) == NULL )
		{
			qFatal( "Error: src_new() failed in effect.cpp!\n" );
		}
	}
}

void Effect::resample( int _i, const sampleFrame * _src_buf,
							sample_rate_t _src_sr,
				sampleFrame * _dst_buf, sample_rate_t _dst_sr,
								f_cnt_t _frames )
{
	if( m_srcState[_i] == NULL )
	{
		return;
	}
	m_srcData[_i].input_frames = _frames;
	m_srcData[_i].output_frames = Engine::mixer()->framesPerPeriod();
	m_srcData[_i].data_in = const_cast<float*>(_src_buf[0].data());
	m_srcData[_i].data_out = _dst_buf[0].data ();
	m_srcData[_i].src_ratio = (double) _dst_sr / _src_sr;
	m_srcData[_i].end_of_input = 0;
	int error;
	if( ( error = src_process( m_srcState[_i], &m_srcData[_i] ) ) )
	{
		qFatal( "Effect::resample(): error while resampling: %s\n",
							src_strerror( error ) );
	}
}