code:
/*
 * TimePos.cpp - Class that encapsulates the position of a note/event in terms of
 *   its bar, beat and tick.
 *
 * Copyright (c) 2004-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net
 *
 * This file is part of LMMS - https://lmms.io
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public
 * License along with this program (see COPYING); if not, write to the
 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301 USA.
 *
 */

#include "TimePos.h"

#include "MeterModel.h"

TimeSig::TimeSig( int num, int denom ) :
	m_num(num),
	m_denom(denom)
{
}

TimeSig::TimeSig( const MeterModel &model ) :
	m_num(model.getNumerator()),
	m_denom(model.getDenominator())
{
}

int TimeSig::numerator() const
{
	return m_num;
}

int TimeSig::denominator() const
{
	return m_denom;
}

TimePos::TimePos( const bar_t bar, const tick_t ticks ) :
	m_ticks( bar * s_ticksPerBar + ticks )
{
}

TimePos::TimePos( const tick_t ticks ) :
	m_ticks( ticks )
{
}

TimePos TimePos::quantize(float bars) const
{
	//The intervals we should snap to, our new position should be a factor of this
	int interval = s_ticksPerBar * bars;
	//The lower position we could snap to
	int lowPos = m_ticks / interval;
	//Offset from the lower position
	int offset = m_ticks % interval;
	//1 if we should snap up, 0 if we shouldn't
	// Ternary expression is making sure that the snap happens in the direction to
	// the right even if m_ticks is negative and the offset is exactly half-way
	// More details on issue #5840 and PR #5847
	int snapUp = ((2 * offset) == -interval)
		? 0
		: (2 * offset) / interval;

	return (lowPos + snapUp) * interval;
}

TimePos TimePos::toAbsoluteBar() const
{
	return getBar() * s_ticksPerBar;
}

TimePos& TimePos::operator+=( const TimePos& time )
{
	m_ticks += time.m_ticks;
	return *this;
}

TimePos& TimePos::operator-=( const TimePos& time )
{
	m_ticks -= time.m_ticks;
	return *this;
}

bar_t TimePos::getBar() const
{
	return m_ticks / s_ticksPerBar;
}

bar_t TimePos::nextFullBar() const
{
	return ( m_ticks + ( s_ticksPerBar - 1 ) ) / s_ticksPerBar;
}

void TimePos::setTicks( tick_t ticks )
{
	m_ticks = ticks;
}

tick_t TimePos::getTicks() const
{
	return m_ticks;
}

TimePos::operator int() const
{
	return m_ticks;
}

tick_t TimePos::ticksPerBeat( const TimeSig &sig ) const
{
	// (number of ticks per bar) divided by (number of beats per bar)
	return ticksPerBar(sig) / sig.numerator();
}

tick_t TimePos::getTickWithinBar( const TimeSig &sig ) const
{
	return m_ticks % ticksPerBar( sig );
}

tick_t TimePos::getBeatWithinBar( const TimeSig &sig ) const
{
	return getTickWithinBar( sig ) / ticksPerBeat( sig );
}

tick_t TimePos::getTickWithinBeat( const TimeSig &sig ) const
{
	return getTickWithinBar( sig ) % ticksPerBeat( sig );
}

f_cnt_t TimePos::frames( const float framesPerTick ) const
{
	if( m_ticks >= 0 )
	{
		return static_cast<f_cnt_t>( m_ticks * framesPerTick );
	}
	return 0;
}

double TimePos::getTimeInMilliseconds( bpm_t beatsPerMinute ) const
{
	return ticksToMilliseconds( getTicks(), beatsPerMinute );
}

TimePos TimePos::fromFrames( const f_cnt_t frames, const float framesPerTick )
{
	return TimePos( static_cast<int>( frames / framesPerTick ) );
}

tick_t TimePos::ticksPerBar()
{
	return s_ticksPerBar;
}

tick_t TimePos::ticksPerBar( const TimeSig &sig )
{
	return DefaultTicksPerBar * sig.numerator() / sig.denominator();
}

int TimePos::stepsPerBar()
{
	int steps = ticksPerBar() / DefaultBeatsPerBar;
	return qMax( 1, steps );
}

void TimePos::setTicksPerBar( tick_t tpb )
{
	s_ticksPerBar = tpb;
}

TimePos TimePos::stepPosition( int step )
{
	return step * ticksPerBar() / stepsPerBar();
}

double TimePos::ticksToMilliseconds( tick_t ticks, bpm_t beatsPerMinute )
{
	return TimePos::ticksToMilliseconds( static_cast<double>(ticks), beatsPerMinute );
}

double TimePos::ticksToMilliseconds(double ticks, bpm_t beatsPerMinute)
{
	// 60 * 1000 / 48 = 1250
	return ( ticks * 1250 ) / beatsPerMinute;
}


code:
/*
 * ToolPlugin.cpp - base class for all tool plugins (graphs, extensions, etc)
 *
 * Copyright (c) 2006-2008 Javier Serrano Polo <jasp00/at/users.sourceforge.net>
 * Copyright (c) 2009 Tobias Doerffel <tobydox/at/users.sourceforge.net>
 *
 * This file is part of LMMS - https://lmms.io
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public
 * License along with this program (see COPYING); if not, write to the
 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301 USA.
 *
 */

#include "ToolPlugin.h"

ToolPlugin::ToolPlugin( const Descriptor * _descriptor, Model * _parent ) :
	Plugin( _descriptor, _parent )
{
}

ToolPlugin::~ToolPlugin()
{
}

ToolPlugin * ToolPlugin::instantiate( const QString & _plugin_name, Model * _parent )
{
	Plugin * p = Plugin::instantiate( _plugin_name, _parent, NULL );
	// check whether instantiated plugin is a tool
	if( p->type() == Plugin::Tool )
	{
		// everything ok, so return pointer
		return dynamic_cast<ToolPlugin *>( p );
	}

	// not quite... so delete plugin
	delete p;
	return NULL;
}

repo_name: CYBERDEViL/LMMS

license: mit


code:
/*
 * Track.cpp - implementation of Track class
 *
 * Copyright (c) 2004-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
 *
 * This file is part of LMMS - https://lmms.io
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public
 * License along with this program (see COPYING); if not, write to the
 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301 USA.
 *
 */

/** \file Track.cpp
 *  \brief Implementation of Track class
 */

#include "Track.h"

#include <QVariant>

#include "AutomationPattern.h"
#include "AutomationTrack.h"
#include "BBTrack.h"
#include "BBTrackContainer.h"
#include "ConfigManager.h"
#include "Engine.h"
#include "InstrumentTrack.h"
#include "SampleTrack.h"
#include "Song.h"

/*! \brief Create a new (empty) track object
 *
 *  The track object is the whole track, linking its contents, its
 *  automation, name, type, and so forth.
 *
 * \param type The type of track (Song Editor or Beat+Bassline Editor)
 * \param tc The track Container object to encapsulate in this track.
 *
 * \todo check the definitions of all the properties - are they OK?
 */
Track::Track( TrackTypes type, TrackContainer * tc ) :
	Model( tc ),                   /*!< The track Model */
	m_trackContainer( tc ),        /*!< The track container object */
	m_type( type ),                /*!< The track type */
	m_name(),                       /*!< The track's name */
	m_mutedModel( false, this, tr( "Mute" ) ), /*!< For controlling track muting */
	m_soloModel( false, this, tr( "Solo" ) ), /*!< For controlling track soloing */
	m_simpleSerializingMode( false ),
	m_trackContentObjects(),        /*!< The track content objects (segments) */
	m_color( 0, 0, 0 ),
	m_hasColor( false )
{
	m_trackContainer->addTrack( this );
	m_height = -1;
}

/*! \brief Destroy this track
 *
 *  If the track container is a Beat+Bassline container, step through
 *  its list of tracks and remove us.
 *
 *  Then delete the TrackContentObject's contents, remove this track from
 *  the track container.
 *
 *  Finally step through this track's automation and forget all of them.
 */
Track::~Track()
{
	lock();
	emit destroyedTrack();

	while( !m_trackContentObjects.isEmpty() )
	{
		delete m_trackContentObjects.last();
	}

	m_trackContainer->removeTrack( this );
	unlock();
}

/*! \brief Create a track based on the given track type and container.
 *
 *  \param tt The type of track to create
 *  \param tc The track container to attach to
 */
Track * Track::create( TrackTypes tt, TrackContainer * tc )
{
	Engine::mixer()->requestChangeInModel();

	Track * t = NULL;

	switch( tt )
	{
		case InstrumentTrack: t = new ::InstrumentTrack( tc ); break;
		case BBTrack: t = new ::BBTrack( tc ); break;
		case SampleTrack: t = new ::SampleTrack( tc ); break;
//		case EVENT_TRACK:
//		case VIDEO_TRACK:
		case AutomationTrack: t = new ::AutomationTrack( tc ); break;
		case HiddenAutomationTrack:
						t = new ::AutomationTrack( tc, true ); break;
		default: break;
	}

	if( tc == Engine::getBBTrackContainer() && t )
	{
		t->createTCOsForBB( Engine::getBBTrackContainer()->numOfBBs()
									- 1 );
	}

	tc->updateAfterTrackAdd();

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

	return t;
}

/*! \brief Create a track inside TrackContainer from track type in a QDomElement and restore state from XML
 *
 *  \param element The QDomElement containing the type of track to create
 *  \param tc The track container to attach to
 */
Track * Track::create( const QDomElement & element, TrackContainer * tc )
{
	Engine::mixer()->requestChangeInModel();

	Track * t = create(
		static_cast<TrackTypes>( element.attribute( "type" ).toInt() ),
									tc );
	if( t != NULL )
	{
		t->restoreState( element );
	}

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

	return t;
}

/*! \brief Clone a track from this track
 *
 */
Track* Track::clone()
{
	QDomDocument doc;
	QDomElement parent = doc.createElement("clone");
	saveState(doc, parent);
	Track* t = create(parent.firstChild().toElement(), m_trackContainer);

	AutomationPattern::resolveAllIDs();
	return t;
}

/*! \brief Save this track's settings to file
 *
 *  We save the track type and its muted state and solo state, then append the track-
 *  specific settings.  Then we iterate through the trackContentObjects
 *  and save all their states in turn.
 *
 *  \param doc The QDomDocument to use to save
 *  \param element The The QDomElement to save into
 *  \todo Does this accurately describe the parameters?  I think not!?
 *  \todo Save the track height
 */
void Track::saveSettings( QDomDocument & doc, QDomElement & element )
{
	if( !m_simpleSerializingMode )
	{
		element.setTagName( "track" );
	}
	element.setAttribute( "type", type() );
	element.setAttribute( "name", name() );
	m_mutedModel.saveSettings( doc, element, "muted" );
	m_soloModel.saveSettings( doc, element, "solo" );
	// Save the mutedBeforeSolo value so we can recover the muted state if any solo was active (issue 5562)
	element.setAttribute( "mutedBeforeSolo", int(m_mutedBeforeSolo) );

	if( m_height >= MINIMAL_TRACK_HEIGHT )
	{
		element.setAttribute( "trackheight", m_height );
	}

	if( m_hasColor )
	{
		element.setAttribute( "color", m_color.name() );
	}

	QDomElement tsDe = doc.createElement( nodeName() );
	// let actual track (InstrumentTrack, bbTrack, sampleTrack etc.) save
	// its settings
	element.appendChild( tsDe );
	saveTrackSpecificSettings( doc, tsDe );

	if( m_simpleSerializingMode )
	{
		m_simpleSerializingMode = false;
		return;
	}

	// now save settings of all TCO's
	for( tcoVector::const_iterator it = m_trackContentObjects.begin();
				it != m_trackContentObjects.end(); ++it )
	{
		( *it )->saveState( doc, element );
	}
}

/*! \brief Load the settings from a file
 *
 *  We load the track's type and muted state and solo state, then clear out our
 *  current TrackContentObject.
 *
 *  Then we step through the QDomElement's children and load the
 *  track-specific settings and trackContentObjects states from it
 *  one at a time.
 *
 *  \param element the QDomElement to load track settings from
 *  \todo Load the track height.
 */
void Track::loadSettings( const QDomElement & element )
{
	if( element.attribute( "type" ).toInt() != type() )
	{
		qWarning( "Current track-type does not match track-type of "
							"settings-node!\n" );
	}

	setName( element.hasAttribute( "name" ) ? element.attribute( "name" ) :
			element.firstChild().toElement().attribute( "name" ) );

	m_mutedModel.loadSettings( element, "muted" );
	m_soloModel.loadSettings( element, "solo" );
	// Get the mutedBeforeSolo value so we can recover the muted state if any solo was active.
	// Older project files that didn't have this attribute will set the value to false (issue 5562)
	m_mutedBeforeSolo = QVariant( element.attribute( "mutedBeforeSolo", "0" ) ).toBool();

	if( element.hasAttribute( "color" ) )
	{
		m_color.setNamedColor( element.attribute( "color" ) );
		m_hasColor = true;
	}

	if( m_simpleSerializingMode )
	{
		QDomNode node = element.firstChild();
		while( !node.isNull() )
		{
			if( node.isElement() && node.nodeName() == nodeName() )
			{
				loadTrackSpecificSettings( node.toElement() );
				break;
			}
			node = node.nextSibling();
		}
		m_simpleSerializingMode = false;
		return;
	}

	while( !m_trackContentObjects.empty() )
	{
		delete m_trackContentObjects.front();
//		m_trackContentObjects.erase( m_trackContentObjects.begin() );
	}

	QDomNode node = element.firstChild();
	while( !node.isNull() )
	{
		if( node.isElement() )
		{
			if( node.nodeName() == nodeName() )
			{
				loadTrackSpecificSettings( node.toElement() );
			}
			else if( node.nodeName() != "muted"
			&& node.nodeName() != "solo"
			&& !node.toElement().attribute( "metadata" ).toInt() )
			{
				TrackContentObject * tco = createTCO(
								TimePos( 0 ) );
				tco->restoreState( node.toElement() );
			}
		}
		node = node.nextSibling();
	}

	int storedHeight = element.attribute( "trackheight" ).toInt();
	if( storedHeight >= MINIMAL_TRACK_HEIGHT )
	{
		m_height = storedHeight;
	}
}

/*! \brief Add another TrackContentObject into this track
 *
 *  \param tco The TrackContentObject to attach to this track.
 */
TrackContentObject * Track::addTCO( TrackContentObject * tco )
{
	m_trackContentObjects.push_back( tco );

	emit trackContentObjectAdded( tco );

	return tco;		// just for convenience
}

/*! \brief Remove a given TrackContentObject from this track
 *
 *  \param tco The TrackContentObject to remove from this track.
 */
void Track::removeTCO( TrackContentObject * tco )
{
	tcoVector::iterator it = std::find( m_trackContentObjects.begin(),
					m_trackContentObjects.end(),
					tco );
	if( it != m_trackContentObjects.end() )
	{
		m_trackContentObjects.erase( it );
		if( Engine::getSong() )
		{
			Engine::getSong()->updateLength();
			Engine::getSong()->setModified();
		}
	}
}

/*! \brief Remove all TCOs from this track */
void Track::deleteTCOs()
{
	while( ! m_trackContentObjects.isEmpty() )
	{
		delete m_trackContentObjects.first();
	}
}

/*! \brief Return the number of trackContentObjects we contain
 *
 *  \return the number of trackContentObjects we currently contain.
 */
int Track::numOfTCOs()
{
	return m_trackContentObjects.size();
}

/*! \brief Get a TrackContentObject by number
 *
 *  If the TCO number is less than our TCO array size then fetch that
 *  numbered object from the array.  Otherwise we warn the user that
 *  we've somehow requested a TCO that is too large, and create a new
 *  TCO for them.
 *  \param tcoNum The number of the TrackContentObject to fetch.
 *  \return the given TrackContentObject or a new one if out of range.
 *  \todo reject TCO numbers less than zero.
 *  \todo if we create a TCO here, should we somehow attach it to the
 *     track?
 */
TrackContentObject * Track::getTCO( int tcoNum )
{
	if( tcoNum < m_trackContentObjects.size() )
	{
		return m_trackContentObjects[tcoNum];
	}
	printf( "called Track::getTCO( %d ), "
			"but TCO %d doesn't exist\n", tcoNum, tcoNum );
	return createTCO( tcoNum * TimePos::ticksPerBar() );

}

/*! \brief Determine the given TrackContentObject's number in our array.
 *
 *  \param tco The TrackContentObject to search for.
 *  \return its number in our array.
 */
int Track::getTCONum( const TrackContentObject * tco )
{
//	for( int i = 0; i < getTrackContentWidget()->numOfTCOs(); ++i )
	tcoVector::iterator it = std::find( m_trackContentObjects.begin(),
					m_trackContentObjects.end(),
					tco );
	if( it != m_trackContentObjects.end() )
	{
/*		if( getTCO( i ) == _tco )
		{
			return i;
		}*/
		return it - m_trackContentObjects.begin();
	}
	qWarning( "Track::getTCONum(...) -> _tco not found!\n" );
	return 0;
}

/*! \brief Retrieve a list of trackContentObjects that fall within a period.
 *
 *  Here we're interested in a range of trackContentObjects that intersect
 *  the given time period.
 *
 *  We return the TCOs we find in order by time, earliest TCOs first.
 *
 *  \param tcoV The list to contain the found trackContentObjects.
 *  \param start The MIDI start time of the range.
 *  \param end   The MIDI endi time of the range.
 */
void Track::getTCOsInRange( tcoVector & tcoV, const TimePos & start,
							const TimePos & end )
{
	for( TrackContentObject* tco : m_trackContentObjects )
	{
		int s = tco->startPosition();
		int e = tco->endPosition();
		if( ( s <= end ) && ( e >= start ) )
		{
			// TCO is within given range
			// Insert sorted by TCO's position
			tcoV.insert(std::upper_bound(tcoV.begin(), tcoV.end(), tco, TrackContentObject::comparePosition),
						tco);
		}
	}
}

/*! \brief Swap the position of two trackContentObjects.
 *
 *  First, we arrange to swap the positions of the two TCOs in the
 *  trackContentObjects list.  Then we swap their start times as well.
 *
 *  \param tcoNum1 The first TrackContentObject to swap.
 *  \param tcoNum2 The second TrackContentObject to swap.
 */
void Track::swapPositionOfTCOs( int tcoNum1, int tcoNum2 )
{
	qSwap( m_trackContentObjects[tcoNum1],
					m_trackContentObjects[tcoNum2] );

	const TimePos pos = m_trackContentObjects[tcoNum1]->startPosition();

	m_trackContentObjects[tcoNum1]->movePosition(
			m_trackContentObjects[tcoNum2]->startPosition() );
	m_trackContentObjects[tcoNum2]->movePosition( pos );
}

void Track::createTCOsForBB( int bb )
{
	while( numOfTCOs() < bb + 1 )
	{
		TimePos position = TimePos( numOfTCOs(), 0 );
		TrackContentObject * tco = createTCO( position );
		tco->changeLength( TimePos( 1, 0 ) );
	}
}

/*! \brief Move all the trackContentObjects after a certain time later by one bar.
 *
 *  \param pos The time at which we want to insert the bar.
 *  \todo if we stepped through this list last to first, and the list was
 *    in ascending order by TCO time, once we hit a TCO that was earlier
 *    than the insert time, we could fall out of the loop early.
 */
void Track::insertBar( const TimePos & pos )
{
	// we'll increase the position of every TCO, positioned behind pos, by
	// one bar
	for( tcoVector::iterator it = m_trackContentObjects.begin();
				it != m_trackContentObjects.end(); ++it )
	{
		if( ( *it )->startPosition() >= pos )
		{
			( *it )->movePosition( (*it)->startPosition() +
						TimePos::ticksPerBar() );
		}
	}
}

/*! \brief Move all the trackContentObjects after a certain time earlier by one bar.
 *
 *  \param pos The time at which we want to remove the bar.
 */
void Track::removeBar( const TimePos & pos )
{
	// we'll decrease the position of every TCO, positioned behind pos, by
	// one bar
	for( tcoVector::iterator it = m_trackContentObjects.begin();
				it != m_trackContentObjects.end(); ++it )
	{
		if( ( *it )->startPosition() >= pos )
		{
			(*it)->movePosition((*it)->startPosition() - TimePos::ticksPerBar());
		}
	}
}

/*! \brief Return the length of the entire track in bars
 *
 *  We step through our list of TCOs and determine their end position,
 *  keeping track of the latest time found in ticks.  Then we return
 *  that in bars by dividing by the number of ticks per bar.
 */
bar_t Track::length() const
{
	// find last end-position
	tick_t last = 0;
	for( tcoVector::const_iterator it = m_trackContentObjects.begin();
				it != m_trackContentObjects.end(); ++it )
	{
		if( Engine::getSong()->isExporting() &&
				( *it )->isMuted() )
		{
			continue;
		}

		const tick_t cur = ( *it )->endPosition();
		if( cur > last )
		{
			last = cur;
		}
	}

	return last / TimePos::ticksPerBar();
}

/*! \brief Invert the track's solo state.
 *
 *  We have to go through all the tracks determining if any other track
 *  is already soloed.  Then we have to save the mute state of all tracks,
 *  and set our mute state to on and all the others to off.
 */
void Track::toggleSolo()
{
	const TrackContainer::TrackList & tl = m_trackContainer->tracks();

	bool soloBefore = false;
	for( TrackContainer::TrackList::const_iterator it = tl.begin();
							it != tl.end(); ++it )
	{
		if( *it != this )
		{
			if( ( *it )->m_soloModel.value() )
			{
				soloBefore = true;
				break;
			}
		}
	}

	const bool solo = m_soloModel.value();
	// Should we use the new behavior of solo or the older/legacy one?
	const bool soloLegacyBehavior = ConfigManager::inst()->value("app", "sololegacybehavior", "0").toInt();

	for( TrackContainer::TrackList::const_iterator it = tl.begin();
							it != tl.end(); ++it )
	{
		if( solo )
		{
			// save mute-state in case no track was solo before
			if( !soloBefore )
			{
				( *it )->m_mutedBeforeSolo = ( *it )->isMuted();
			}
			// Don't mute AutomationTracks (keep their original state) unless we are on the sololegacybehavior mode
			if( *it == this )
			{
				( *it )->setMuted( false );
			}
			else if( soloLegacyBehavior || ( *it )->type() != AutomationTrack )
			{
				( *it )->setMuted( true );
			}
			if( *it != this )
			{
				( *it )->m_soloModel.setValue( false );
			}
		}
		else if( !soloBefore )
		{
			// Unless we are on the sololegacybehavior mode, only restores the
			// mute state if the track isn't an Automation Track
			if( soloLegacyBehavior || ( *it )->type() != AutomationTrack )
			{
				( *it )->setMuted( ( *it )->m_mutedBeforeSolo );
			}
		}
	}
}

void Track::trackColorChanged( QColor & c )
{
	for (int i = 0; i < numOfTCOs(); i++)
	{
		m_trackContentObjects[i]->updateColor();
	}
	m_hasColor = true;
	m_color = c;
}

void Track::trackColorReset()
{
	for (int i = 0; i < numOfTCOs(); i++)
	{
		m_trackContentObjects[i]->updateColor();
	}
	m_hasColor = false;
}

BoolModel *Track::getMutedModel()
{
	return &m_mutedModel;
}


code:
/*
 * TrackContainer.cpp - implementation of base class for all trackcontainers
 *                      like Song-Editor, BB-Editor...
 *
 * Copyright (c) 2004-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
 *
 * This file is part of LMMS - https://lmms.io
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public
 * License along with this program (see COPYING); if not, write to the
 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301 USA.
 *
 */

#include <QApplication>
#include <QProgressDialog>
#include <QDomElement>
#include <QWriteLocker>

#include "AutomationPattern.h"
#include "AutomationTrack.h"
#include "BBTrack.h"
#include "BBTrackContainer.h"
#include "embed.h"
#include "TrackContainer.h"
#include "InstrumentTrack.h"
#include "Song.h"

#include "GuiApplication.h"
#include "MainWindow.h"
#include "TextFloat.h"

TrackContainer::TrackContainer() :
	Model( NULL ),
	JournallingObject(),
	m_tracksMutex(),
	m_tracks()
{
}

TrackContainer::~TrackContainer()
{
	clearAllTracks();
}

void TrackContainer::saveSettings( QDomDocument & _doc, QDomElement & _this )
{
	_this.setTagName( classNodeName() );
	_this.setAttribute( "type", nodeName() );

	// save settings of each track
	m_tracksMutex.lockForRead();
	for( int i = 0; i < m_tracks.size(); ++i )
	{
		m_tracks[i]->saveState( _doc, _this );
	}
	m_tracksMutex.unlock();
}

void TrackContainer::loadSettings( const QDomElement & _this )
{
	bool journalRestore = _this.parentNode().nodeName() == "journaldata";
	if( journalRestore )
	{
		clearAllTracks();
	}

	static QProgressDialog * pd = NULL;
	bool was_null = ( pd == NULL );
	if( !journalRestore && gui != nullptr )
	{
		if( pd == NULL )
		{
			pd = new QProgressDialog( tr( "Loading project..." ),
						tr( "Cancel" ), 0,
						Engine::getSong()->getLoadingTrackCount(),
						gui->mainWindow() );
			pd->setWindowModality( Qt::ApplicationModal );
			pd->setWindowTitle( tr( "Please wait..." ) );
			pd->show();
		}
	}

	QDomNode node = _this.firstChild();
	while( !node.isNull() )
	{
		if( pd != NULL )
		{
			pd->setValue( pd->value() + 1 );
			QCoreApplication::instance()->processEvents(
						QEventLoop::AllEvents, 100 );
			if( pd->wasCanceled() )
			{
				if ( gui )
				{
					TextFloat::displayMessage( tr( "Loading cancelled" ),
					tr( "Project loading was cancelled." ),
					embed::getIconPixmap( "project_file", 24, 24 ),
					2000 );
				}
				Engine::getSong()->loadingCancelled();
				break;
			}
		}

		if( node.isElement() &&
			!node.toElement().attribute( "metadata" ).toInt() )
		{
			QString trackName = node.toElement().hasAttribute( "name" ) ?
						node.toElement().attribute( "name" ) :
						node.firstChild().toElement().attribute( "name" );
			if( pd != NULL )
			{
				pd->setLabelText( tr("Loading Track %1 (%2/Total %3)").arg( trackName ).
						  arg( pd->value() + 1 ).arg( Engine::getSong()->getLoadingTrackCount() ) );
			}
			Track::create( node.toElement(), this );
		}
		node = node.nextSibling();
	}

	if( pd != NULL )
	{
		if( was_null )
		{
			delete pd;
			pd = NULL;
		}
	}
}

int TrackContainer::countTracks( Track::TrackTypes _tt ) const
{
	int cnt = 0;
	m_tracksMutex.lockForRead();
	for( int i = 0; i < m_tracks.size(); ++i )
	{
		if( m_tracks[i]->type() == _tt || _tt == Track::NumTrackTypes )
		{
			++cnt;
		}
	}
	m_tracksMutex.unlock();
	return( cnt );
}

void TrackContainer::addTrack( Track * _track )
{
	if( _track->type() != Track::HiddenAutomationTrack )
	{
		_track->lock();
		m_tracksMutex.lockForWrite();
		m_tracks.push_back( _track );
		m_tracksMutex.unlock();
		_track->unlock();
		emit trackAdded( _track );
	}
}

void TrackContainer::removeTrack( Track * _track )
{
	// need a read locker to ensure that m_tracks doesn't change after reading index.
	//   After checking that index != -1, we need to upgrade the lock to a write locker before changing m_tracks.
	//   But since Qt offers no function to promote a read lock to a write lock, we must start with the write locker.
	QWriteLocker lockTracksAccess(&m_tracksMutex);
	int index = m_tracks.indexOf( _track );
	if( index != -1 )
	{
		// If the track is solo, all other tracks are muted. Change this before removing the solo track:
		if (_track->isSolo()) {
			_track->setSolo(false);
		}
		m_tracks.remove( index );
		lockTracksAccess.unlock();

		if( Engine::getSong() )
		{
			Engine::getSong()->setModified();
		}
	}
}

void TrackContainer::updateAfterTrackAdd()
{
}

void TrackContainer::clearAllTracks()
{
	//m_tracksMutex.lockForWrite();
	while( !m_tracks.isEmpty() )
	{
		delete m_tracks.first();
	}
	//m_tracksMutex.unlock();
}

bool TrackContainer::isEmpty() const
{
	for( TrackList::const_iterator it = m_tracks.begin();
						it != m_tracks.end(); ++it )
	{
		if( !( *it )->getTCOs().isEmpty() )
		{
			return false;
		}
	}
	return true;
}

AutomatedValueMap TrackContainer::automatedValuesAt(TimePos time, int tcoNum) const
{
	return automatedValuesFromTracks(tracks(), time, tcoNum);
}

AutomatedValueMap TrackContainer::automatedValuesFromTracks(const TrackList &tracks, TimePos time, int tcoNum)
{
	Track::tcoVector tcos;

	for (Track* track: tracks)
	{
		if (track->isMuted()) {
			continue;
		}

		switch(track->type())
		{
		case Track::AutomationTrack:
		case Track::HiddenAutomationTrack:
		case Track::BBTrack:
			if (tcoNum < 0) {
				track->getTCOsInRange(tcos, 0, time);
			} else {
				Q_ASSERT(track->numOfTCOs() > tcoNum);
				tcos << track->getTCO(tcoNum);
			}
		default:
			break;
		}
	}

	AutomatedValueMap valueMap;

	Q_ASSERT(std::is_sorted(tcos.begin(), tcos.end(), TrackContentObject::comparePosition));

	for(TrackContentObject* tco : tcos)
	{
		if (tco->isMuted() || tco->startPosition() > time) {
			continue;
		}

		if (auto* p = dynamic_cast<AutomationPattern *>(tco))
		{
			if (! p->hasAutomation()) {
				continue;
			}
			TimePos relTime = time - p->startPosition();
			if (! p->getAutoResize()) {
				relTime = qMin(relTime, p->length());
			}
			float value = p->valueAt(relTime);

			for (AutomatableModel* model : p->objects())
			{
				valueMap[model] = value;
			}
		}
		else if (auto* bb = dynamic_cast<BBTCO *>(tco))
		{
			auto bbIndex = dynamic_cast<class BBTrack*>(bb->getTrack())->index();
			auto bbContainer = Engine::getBBTrackContainer();

			TimePos bbTime = time - tco->startPosition();
			bbTime = std::min(bbTime, tco->length());
			bbTime = bbTime % (bbContainer->lengthOfBB(bbIndex) * TimePos::ticksPerBar());

			auto bbValues = bbContainer->automatedValuesAt(bbTime, bbIndex);
			for (auto it=bbValues.begin(); it != bbValues.end(); it++)
			{
				// override old values, bb track with the highest index takes precedence
				valueMap[it.key()] = it.value();
			}
		}
		else
		{
			continue;
		}
	}

	return valueMap;
};


code:
/*
 * TrackContentObject.cpp - implementation of TrackContentObject class
 *
 * Copyright (c) 2004-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
 *
 * This file is part of LMMS - https://lmms.io
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public
 * License along with this program (see COPYING); if not, write to the
 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301 USA.
 *
 */

#include "TrackContentObject.h"

#include <QDomDocument>

#include "AutomationEditor.h"
#include "AutomationPattern.h"
#include "Engine.h"
#include "GuiApplication.h"
#include "Song.h"

/*! \brief Create a new TrackContentObject
 *
 *  Creates a new track content object for the given track.
 *
 * \param _track The track that will contain the new object
 */
TrackContentObject::TrackContentObject( Track * track ) :
	Model( track ),
	m_track( track ),
	m_startPosition(),
	m_length(),
	m_mutedModel( false, this, tr( "Mute" ) ),
	m_selectViewOnCreate( false ),
	m_color( 128, 128, 128 ),
	m_useCustomClipColor( false )
{
	if( getTrack() )
	{
		getTrack()->addTCO( this );
	}
	setJournalling( false );
	movePosition( 0 );
	changeLength( 0 );
	setJournalling( true );
}

/*! \brief Destroy a TrackContentObject
 *
 *  Destroys the given track content object.
 *
 */
TrackContentObject::~TrackContentObject()
{
	emit destroyedTCO();

	if( getTrack() )
	{
		getTrack()->removeTCO( this );
	}
}

/*! \brief Move this TrackContentObject's position in time
 *
 *  If the track content object has moved, update its position.  We
 *  also add a journal entry for undo and update the display.
 *
 * \param _pos The new position of the track content object.
 */
void TrackContentObject::movePosition( const TimePos & pos )
{
	TimePos newPos = qMax(0, pos.getTicks());
	if (m_startPosition != newPos)
	{
		Engine::mixer()->requestChangeInModel();
		m_startPosition = newPos;
		Engine::mixer()->doneChangeInModel();
		Engine::getSong()->updateLength();
		emit positionChanged();
	}
}

/*! \brief Change the length of this TrackContentObject
 *
 *  If the track content object's length has changed, update it.  We
 *  also add a journal entry for undo and update the display.
 *
 * \param _length The new length of the track content object.
 */
void TrackContentObject::changeLength( const TimePos & length )
{
	m_length = length;
	Engine::getSong()->updateLength();
	emit lengthChanged();
}

bool TrackContentObject::comparePosition(const TrackContentObject *a, const TrackContentObject *b)
{
	return a->startPosition() < b->startPosition();
}

/*! \brief Copies the state of a TrackContentObject to another TrackContentObject
 *
 *  This method copies the state of a TCO to another TCO
 */
void TrackContentObject::copyStateTo( TrackContentObject *src, TrackContentObject *dst )
{
	// If the node names match we copy the state
	if( src->nodeName() == dst->nodeName() ){
		QDomDocument doc;
		QDomElement parent = doc.createElement( "StateCopy" );
		src->saveState( doc, parent );

		const TimePos pos = dst->startPosition();
		dst->restoreState( parent.firstChild().toElement() );
		dst->movePosition( pos );

		AutomationPattern::resolveAllIDs();
		GuiApplication::instance()->automationEditor()->m_editor->updateAfterPatternChange();
	}
}

/*! \brief Mutes this TrackContentObject
 *
 *  Restore the previous state of this track content object.  This will
 *  restore the position or the length of the track content object
 *  depending on what was changed.
 *
 * \param _je The journal entry to undo
 */
void TrackContentObject::toggleMute()
{
	m_mutedModel.setValue( !m_mutedModel.value() );
	emit dataChanged();
}

TimePos TrackContentObject::startTimeOffset() const
{
	return m_startTimeOffset;
}

void TrackContentObject::setStartTimeOffset( const TimePos &startTimeOffset )
{
	m_startTimeOffset = startTimeOffset;
}

// Update TCO color if it follows the track color
void TrackContentObject::updateColor()
{
	if( ! m_useCustomClipColor )
	{
		emit trackColorChanged();
	}
}

void TrackContentObject::useCustomClipColor( bool b )
{
	m_useCustomClipColor = b;
	updateColor();
}

bool TrackContentObject::hasColor()
{
	return usesCustomClipColor() || getTrack()->useColor();
}


code:
#include "ValueBuffer.h"

#include "interpolation.h"

ValueBuffer::ValueBuffer()
{}

ValueBuffer::ValueBuffer(int length)
	: std::vector<float>(length)
{}

void ValueBuffer::fill(float value)
{
	std::fill(begin(), end(), value);
}

float ValueBuffer::value(int offset) const
{
	return at(offset % length());
}

const float *ValueBuffer::values() const
{
	return data();
}

float *ValueBuffer::values()
{
	return data();
}

int ValueBuffer::length() const
{
	return size();
}

void ValueBuffer::interpolate(float start, float end_)
{
	float i = 0;
	std::generate(begin(), end(), [&]() {
		return linearInterpolate( start, end_, i++ / length());
	});
}


code:
/*
 * VstSyncController.cpp - manage synchronization between LMMS and VST plugins
 *
 * Copyright (c) 2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
 * Copyright (c) 2013 Mike Choi <rdavidian71/at/gmail/dot/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 <QDebug>

#include "ConfigManager.h"
#include "Engine.h"
#include "Mixer.h"
#include "VstSyncController.h"
#include "RemotePlugin.h"

#ifndef USE_QT_SHMEM
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#endif

VstSyncController::VstSyncController() :
	m_syncData( NULL ),
	m_shmID( -1 ),
	m_shm( "/usr/bin/lmms" )
{
	if( ConfigManager::inst()->value( "ui", "syncvstplugins" ).toInt() )
	{
		connect( Engine::mixer(), SIGNAL( sampleRateChanged() ), this, SLOT( updateSampleRate() ) );

#ifdef USE_QT_SHMEM
		if ( m_shm.create( sizeof( VstSyncData ) ) )
		{
			m_syncData = (VstSyncData*) m_shm.data();
		}
		else
		{
			qWarning() << QString( "Failed to allocate shared memory for VST sync: %1" ).arg( m_shm.errorString() );
		}
#else
		key_t key; // make the key:
		if( ( key = ftok( VST_SNC_SHM_KEY_FILE, 'R' ) ) == -1 )
		{
				qWarning( "VstSyncController: ftok() failed" );
		}
		else
		{	// connect to shared memory segment
			if( ( m_shmID = shmget( key, sizeof( VstSyncData ), 0644 | IPC_CREAT ) ) == -1 )
			{
				qWarning( "VstSyncController: shmget() failed" );
			}
			else
			{		// attach segment
				m_syncData = (VstSyncData *)shmat( m_shmID, 0, 0 );
				if( m_syncData == (VstSyncData *)( -1 ) )
				{
					qWarning( "VstSyncController: shmat() failed" );
				}
			}
		}
#endif
	}
	else
	{
		qWarning( "VST sync support disabled in your configuration" );
	}

	if( m_syncData == NULL )
	{
		m_syncData = new VstSyncData;
		m_syncData->hasSHM = false;
	}
	else
	{
		m_syncData->hasSHM = true;
	}

	m_syncData->isPlaying = false;
	m_syncData->m_bufferSize = Engine::mixer()->framesPerPeriod();
	m_syncData->timeSigNumer = 4;
	m_syncData->timeSigDenom = 4;

	updateSampleRate();
}

VstSyncController::~VstSyncController()
{
	if( m_syncData->hasSHM == false )
	{
		delete m_syncData;
	}
	else
	{
#ifdef USE_QT_SHMEM
		if( m_shm.data() )
		{
			// detach shared memory, delete it:
			m_shm.detach();
		}
#else
		if( shmdt( m_syncData ) != -1 )
		{
			shmctl( m_shmID, IPC_RMID, NULL );
		}
		else
		{
			qWarning( "VstSyncController: shmdt() failed" );
		}
#endif
	}
}

void VstSyncController::setAbsolutePosition( double ticks )
{
#ifdef VST_SNC_LATENCY
	m_syncData->ppqPos = ( ( ticks + 0 ) / 48.0 ) - m_syncData->m_latency;
#else
	m_syncData->ppqPos = ( ( ticks + 0 ) / 48.0 );
#endif
}

void VstSyncController::setTempo( int newTempo )
{
	m_syncData->m_bpm = newTempo;

#ifdef VST_SNC_LATENCY
	m_syncData->m_latency = m_syncData->m_bufferSize * newTempo / ( (float) m_syncData->m_sampleRate * 60 );
#endif

}

void VstSyncController::startCycle( int startTick, int endTick )
{
	m_syncData->isCycle = true;
	m_syncData->cycleStart = startTick / (float)48;
	m_syncData->cycleEnd = endTick / (float)48;
}

void VstSyncController::update()
{
	m_syncData->m_bufferSize = Engine::mixer()->framesPerPeriod();

#ifdef VST_SNC_LATENCY
	m_syncData->m_latency = m_syncData->m_bufferSize * m_syncData->m_bpm / ( (float) m_syncData->m_sampleRate * 60 );
#endif
}

void VstSyncController::updateSampleRate()
{
	m_syncData->m_sampleRate = Engine::mixer()->processingSampleRate();

#ifdef VST_SNC_LATENCY
	m_syncData->m_latency = m_syncData->m_bufferSize * m_syncData->m_bpm / ( (float) m_syncData->m_sampleRate * 60 );
#endif
}


code:
/*
 * AudioAlsa.cpp - device-class which implements ALSA-PCM-output
 *
 * Copyright (c) 2004-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
 *
 * This file is part of LMMS - https://lmms.io
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public
 * License along with this program (see COPYING); if not, write to the
 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301 USA.
 *
 */

#include <QComboBox>
#include <QLineEdit>

#include "AudioAlsa.h"

#ifdef LMMS_HAVE_ALSA

#include "endian_handling.h"
#include "ConfigManager.h"
#include "Engine.h"
#include "Mixer.h"
#include "gui_templates.h"

AudioAlsa::AudioAlsa( bool & _success_ful, Mixer*  _mixer ) :
	AudioDevice( qBound<ch_cnt_t>(
		DEFAULT_CHANNELS,
		ConfigManager::inst()->value( "audioalsa", "channels" ).toInt(),
		SURROUND_CHANNELS ), _mixer ),
	m_handle( NULL ),
	m_hwParams( NULL ),
	m_swParams( NULL ),
	m_convertEndian( false )
{
	_success_ful = false;

	if( setenv( "PULSE_ALSA_HOOK_CONF", "/dev/null", 0 ) )
	{
		fprintf( stderr,
		"Could not avoid possible interception by PulseAudio\n" );
	}

	int err;

	if( ( err = snd_pcm_open( &m_handle,
					probeDevice().toLatin1().constData(),
						SND_PCM_STREAM_PLAYBACK,
						0 ) ) < 0 )
	{
		printf( "Playback open error: %s\n", snd_strerror( err ) );
		return;
	}

	snd_pcm_hw_params_malloc( &m_hwParams );
	snd_pcm_sw_params_malloc( &m_swParams );

	if( ( err = setHWParams( channels(),
					SND_PCM_ACCESS_RW_INTERLEAVED ) ) < 0 )
	{
		printf( "Setting of hwparams failed: %s\n",
							snd_strerror( err ) );
		return;
	}
	if( ( err = setSWParams() ) < 0 )
	{
		printf( "Setting of swparams failed: %s\n",
							snd_strerror( err ) );
		return;
	}

	// set FD_CLOEXEC flag for all file descriptors so forked processes
	// do not inherit them
	struct pollfd * ufds;
	int count = snd_pcm_poll_descriptors_count( m_handle );
	ufds = new pollfd[count];
	snd_pcm_poll_descriptors( m_handle, ufds, count );
	for( int i = 0; i < qMax( 3, count ); ++i )
	{
		const int fd = ( i >= count ) ? ufds[0].fd+i : ufds[i].fd;
		int oldflags = fcntl( fd, F_GETFD, 0 );
		if( oldflags < 0 )
			continue;
		oldflags |= FD_CLOEXEC;
		fcntl( fd, F_SETFD, oldflags );
	}
	delete[] ufds;
	_success_ful = true;
}

AudioAlsa::~AudioAlsa()
{
	stopProcessing();
	if( m_handle != NULL )
	{
		snd_pcm_close( m_handle );
	}

	if( m_hwParams != NULL )
	{
		snd_pcm_hw_params_free( m_hwParams );
	}

	if( m_swParams != NULL )
	{
		snd_pcm_sw_params_free( m_swParams );
	}
}

QString AudioAlsa::probeDevice()
{
	QString dev = ConfigManager::inst()->value( "audioalsa", "device" );
	if( dev == "" )
	{
		if( getenv( "AUDIODEV" ) != NULL )
		{
			return getenv( "AUDIODEV" );
		}
		return "default";
	}
	return dev;
}

/**
 * @brief Creates a list of all available devices.
 *
 * Uses the hints API of ALSA to collect all devices. This also includes plug
 * devices. The reason to collect these and not the raw hardware devices
 * (e.g. hw:0,0) is that hardware devices often have a very limited number of
 * supported formats, etc. Plugs on the other hand are software components that
 * map all types of formats and inputs to the hardware and therefore they are
 * much more flexible and more what we want.
 *
 * Further helpful info http://jan.newmarch.name/LinuxSound/Sampled/Alsa/.
 *
 * @return A collection of devices found on the system.
 */
AudioAlsa::DeviceInfoCollection AudioAlsa::getAvailableDevices()
{
	DeviceInfoCollection deviceInfos;

	char **hints;

	/* Enumerate sound devices */
	int err = snd_device_name_hint(-1, "pcm", (void***)&hints);
	if (err != 0)
	{
		return deviceInfos;
	}

	char** n = hints;
	while (*n != NULL)
	{
		char *name = snd_device_name_get_hint(*n, "NAME");
		char *description = snd_device_name_get_hint(*n, "DESC");

		if (name != 0 && description != 0)
		{
			deviceInfos.push_back(DeviceInfo(QString(name), QString(description)));
		}

		free(name);
		free(description);

		n++;
	}

	//Free the hint buffer
	snd_device_name_free_hint((void**)hints);

	return deviceInfos;
}

int AudioAlsa::handleError( int _err )
{
	if( _err == -EPIPE )
	{
		// under-run
		_err = snd_pcm_prepare( m_handle );
		if( _err < 0 )
			printf( "Can't recover from underrun, prepare "
					"failed: %s\n", snd_strerror( _err ) );
		return ( 0 );
	}
#ifdef ESTRPIPE
	else if( _err == -ESTRPIPE )
	{
		while( ( _err = snd_pcm_resume( m_handle ) ) == -EAGAIN )
		{
			sleep( 1 );	// wait until the suspend flag
					// is released
		}

		if( _err < 0 )
		{
			_err = snd_pcm_prepare( m_handle );
			if( _err < 0 )
				printf( "Can't recover from suspend, prepare "
					"failed: %s\n", snd_strerror( _err ) );
		}
		return ( 0 );
	}
#endif
	return _err;
}

void AudioAlsa::startProcessing()
{
	if( !isRunning() )
	{
		start( QThread::HighPriority );
	}
}

void AudioAlsa::stopProcessing()
{
	stopProcessingThread( this );
}

void AudioAlsa::applyQualitySettings()
{
	if( hqAudio() )
	{
		setSampleRate( Engine::mixer()->processingSampleRate() );

		if( m_handle != NULL )
		{
			snd_pcm_close( m_handle );
		}

		int err;
		if( ( err = snd_pcm_open( &m_handle,
					probeDevice().toLatin1().constData(),
						SND_PCM_STREAM_PLAYBACK,
								0 ) ) < 0 )
		{
			printf( "Playback open error: %s\n",
							snd_strerror( err ) );
			return;
		}

		if( ( err = setHWParams( channels(),
					SND_PCM_ACCESS_RW_INTERLEAVED ) ) < 0 )
		{
			printf( "Setting of hwparams failed: %s\n",
							snd_strerror( err ) );
			return;
		}
		if( ( err = setSWParams() ) < 0 )
		{
			printf( "Setting of swparams failed: %s\n",
							snd_strerror( err ) );
			return;
		}
	}

	AudioDevice::applyQualitySettings();
}

void AudioAlsa::run()
{
	surroundSampleFrame * temp =
		new surroundSampleFrame[mixer()->framesPerPeriod()];
	int_sample_t * outbuf =
			new int_sample_t[mixer()->framesPerPeriod() *
								channels()];
	int_sample_t * pcmbuf = new int_sample_t[m_periodSize * channels()];

	int outbuf_size = mixer()->framesPerPeriod() * channels();
	int outbuf_pos = 0;
	int pcmbuf_size = m_periodSize * channels();

	bool quit = false;
	while( quit == false )
	{
		int_sample_t * ptr = pcmbuf;
		int len = pcmbuf_size;
		while( len )
		{
			if( outbuf_pos == 0 )
			{
				// frames depend on the sample rate
				const fpp_t frames = getNextBuffer( temp );
				if( !frames )
				{
					quit = true;
					memset( ptr, 0, len
						* sizeof( int_sample_t ) );
					break;
				}
				outbuf_size = frames * channels();

				convertToS16( temp, frames,
						mixer()->masterGain(),
						outbuf,
						m_convertEndian );
			}
			int min_len = qMin( len, outbuf_size - outbuf_pos );
			memcpy( ptr, outbuf + outbuf_pos,
					min_len * sizeof( int_sample_t ) );
			ptr += min_len;
			len -= min_len;
			outbuf_pos += min_len;
			outbuf_pos %= outbuf_size;
		}

		f_cnt_t frames = m_periodSize;
		ptr = pcmbuf;

		while( frames )
		{
			int err = snd_pcm_writei( m_handle, ptr, frames );

			if( err == -EAGAIN )
			{
				continue;
			}

			if( err < 0 )
			{
				if( handleError( err ) < 0 )
				{
					printf( "Write error: %s\n",
							snd_strerror( err ) );
				}
				break;	// skip this buffer
			}
			ptr += err * channels();
			frames -= err;
		}
	}

	delete[] temp;
	delete[] outbuf;
	delete[] pcmbuf;
}

int AudioAlsa::setHWParams( const ch_cnt_t _channels, snd_pcm_access_t _access )
{
	int err, dir;

	// choose all parameters
	if( ( err = snd_pcm_hw_params_any( m_handle, m_hwParams ) ) < 0 )
	{
		printf( "Broken configuration for playback: no configurations "
				"available: %s\n", snd_strerror( err ) );
		return err;
	}

	// set the interleaved read/write format
	if( ( err = snd_pcm_hw_params_set_access( m_handle, m_hwParams,
							_access ) ) < 0 )
	{
		printf( "Access type not available for playback: %s\n",
							snd_strerror( err ) );
		return err;
	}

	// set the sample format
	if( ( snd_pcm_hw_params_set_format( m_handle, m_hwParams,
						SND_PCM_FORMAT_S16_LE ) ) < 0 )
	{
		if( ( snd_pcm_hw_params_set_format( m_handle, m_hwParams,
						SND_PCM_FORMAT_S16_BE ) ) < 0 )
		{
			printf( "Neither little- nor big-endian available for "
					"playback: %s\n", snd_strerror( err ) );
			return err;
		}
		m_convertEndian = isLittleEndian();
	}
	else
	{
		m_convertEndian = !isLittleEndian();
	}

	// set the count of channels
	if( ( err = snd_pcm_hw_params_set_channels( m_handle, m_hwParams,
							_channels ) ) < 0 )
	{
		printf( "Channel count (%i) not available for playbacks: %s\n"
				"(Does your soundcard not support surround?)\n",
					_channels, snd_strerror( err ) );
		return err;
	}

	// set the sample rate
	if( ( err = snd_pcm_hw_params_set_rate( m_handle, m_hwParams,
						sampleRate(), 0 ) ) < 0 )
	{
		if( ( err = snd_pcm_hw_params_set_rate( m_handle, m_hwParams,
				mixer()->baseSampleRate(), 0 ) ) < 0 )
		{
			printf( "Could not set sample rate: %s\n",
							snd_strerror( err ) );
			return err;
		}
	}

	m_periodSize = mixer()->framesPerPeriod();
	m_bufferSize = m_periodSize * 8;
	dir = 0;
	err = snd_pcm_hw_params_set_period_size_near( m_handle, m_hwParams,
							&m_periodSize, &dir );
	if( err < 0 )
	{
		printf( "Unable to set period size %lu for playback: %s\n",
					m_periodSize, snd_strerror( err ) );
		return err;
	}
	dir = 0;
	err = snd_pcm_hw_params_get_period_size( m_hwParams, &m_periodSize,
									&dir );
	if( err < 0 )
	{
		printf( "Unable to get period size for playback: %s\n",
							snd_strerror( err ) );
	}

	dir = 0;
	err = snd_pcm_hw_params_set_buffer_size_near( m_handle, m_hwParams,
								&m_bufferSize );
	if( err < 0 )
	{
		printf( "Unable to set buffer size %lu for playback: %s\n",
					m_bufferSize, snd_strerror( err ) );
		return ( err );
	}
	err = snd_pcm_hw_params_get_buffer_size( m_hwParams, &m_bufferSize );

	if( 2 * m_periodSize > m_bufferSize )
	{
		printf( "buffer to small, could not use\n" );
		return ( err );
	}

	// write the parameters to device
	err = snd_pcm_hw_params( m_handle, m_hwParams );
	if( err < 0 )
	{
		printf( "Unable to set hw params for playback: %s\n",
							snd_strerror( err ) );
		return ( err );
	}

	return ( 0 );	// all ok
}

int AudioAlsa::setSWParams()
{
	int err;

	// get the current swparams
	if( ( err = snd_pcm_sw_params_current( m_handle, m_swParams ) ) < 0 )
	{
		printf( "Unable to determine current swparams for playback: %s"
						"\n", snd_strerror( err ) );
		return err;
	}

	// start the transfer when a period is full
	if( ( err = snd_pcm_sw_params_set_start_threshold( m_handle,
					m_swParams, m_periodSize ) ) < 0 )
	{
		printf( "Unable to set start threshold mode for playback: %s\n",
							snd_strerror( err ) );
		return err;
	}

	// allow the transfer when at least m_periodSize samples can be
	// processed
	if( ( err = snd_pcm_sw_params_set_avail_min( m_handle, m_swParams,
							m_periodSize ) ) < 0 )
	{
		printf( "Unable to set avail min for playback: %s\n",
							snd_strerror( err ) );
		return err;
	}

	// align all transfers to 1 sample

#if SND_LIB_VERSION < ((1<<16)|(0)|16)
	if( ( err = snd_pcm_sw_params_set_xfer_align( m_handle,
							m_swParams, 1 ) ) < 0 )
	{
		printf( "Unable to set transfer align for playback: %s\n",
							snd_strerror( err ) );
		return err;
	}
#endif

	// write the parameters to the playback device
	if( ( err = snd_pcm_sw_params( m_handle, m_swParams ) ) < 0 )
	{
		printf( "Unable to set sw params for playback: %s\n",
							snd_strerror( err ) );
		return err;
	}

	return 0;	// all ok
}

#endif


code:
/*
 * AudioDevice.cpp - base-class for audio-devices used by LMMS-mixer
 *
 * Copyright (c) 2004-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
 *
 * This file is part of LMMS - https://lmms.io
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public
 * License along with this program (see COPYING); if not, write to the
 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301 USA.
 *
 */

#include <cstring>

#include "AudioDevice.h"
#include "ConfigManager.h"
#include "debug.h"
#include "Mixer.h"

AudioDevice::AudioDevice( const ch_cnt_t _channels, Mixer*  _mixer ) :
	m_supportsCapture( false ),
	m_sampleRate( _mixer->processingSampleRate() ),
	m_channels( _channels ),
	m_mixer( _mixer ),
	m_buffer( new surroundSampleFrame[mixer()->framesPerPeriod()] )
{
	int error;
	if( ( m_srcState = src_new(
		mixer()->currentQualitySettings().libsrcInterpolation(),
				SURROUND_CHANNELS, &error ) ) == NULL )
	{
		printf( "Error: src_new() failed in audio_device.cpp!\n" );
	}
}

AudioDevice::~AudioDevice()
{
	src_delete( m_srcState );
	delete[] m_buffer;

	m_devMutex.tryLock();
	unlock();
}

void AudioDevice::processNextBuffer()
{
	const fpp_t frames = getNextBuffer( m_buffer );
	if( frames )
	{
		writeBuffer( m_buffer, frames, mixer()->masterGain() );
	}
	else
	{
		m_inProcess = false;
	}
}

fpp_t AudioDevice::getNextBuffer( surroundSampleFrame * _ab )
{
	fpp_t frames = mixer()->framesPerPeriod();
	const surroundSampleFrame * b = mixer()->nextBuffer();
	if( !b )
	{
		return 0;
	}

	// make sure, no other thread is accessing device
	lock();

	// resample if necessary
	if( mixer()->processingSampleRate() != m_sampleRate )
	{
		frames = resample( b, frames, _ab, mixer()->processingSampleRate(),
				    	   m_sampleRate );
	}
	else
	{
		memcpy( _ab, b, frames * sizeof( surroundSampleFrame ) );
	}

	// release lock
	unlock();

	if( mixer()->hasFifoWriter() )
	{
		delete[] b;
	}

	return frames;
}

void AudioDevice::stopProcessing()
{
	if( mixer()->hasFifoWriter() )
	{
		while( m_inProcess )
		{
			processNextBuffer();
		}
	}
}

void AudioDevice::stopProcessingThread( QThread * thread )
{
	if( !thread->wait( 30000 ) )
	{
		fprintf( stderr, "Terminating audio device thread\n" );
		thread->terminate();
		if( !thread->wait( 1000 ) )
		{
			fprintf( stderr, "Thread not terminated yet\n" );
		}
	}
}

void AudioDevice::applyQualitySettings()
{
	src_delete( m_srcState );

	int error;
	if( ( m_srcState = src_new(
		mixer()->currentQualitySettings().libsrcInterpolation(),
				SURROUND_CHANNELS, &error ) ) == NULL )
	{
		printf( "Error: src_new() failed in audio_device.cpp!\n" );
	}
}

void AudioDevice::registerPort( AudioPort * )
{
}

void AudioDevice::unregisterPort( AudioPort * _port )
{
}

void AudioDevice::renamePort( AudioPort * )
{
}

fpp_t AudioDevice::resample( const surroundSampleFrame * _src,
						const fpp_t _frames,
						surroundSampleFrame * _dst,
						const sample_rate_t _src_sr,
						const sample_rate_t _dst_sr )
{
	if( m_srcState == NULL )
	{
		return _frames;
	}
	m_srcData.input_frames = _frames;
	m_srcData.output_frames = _frames;
	m_srcData.data_in = const_cast<float*>(_src[0].data());
	m_srcData.data_out = _dst[0].data ();
	m_srcData.src_ratio = (double) _dst_sr / _src_sr;
	m_srcData.end_of_input = 0;
	int error;
	if( ( error = src_process( m_srcState, &m_srcData ) ) )
	{
		printf( "AudioDevice::resample(): error while resampling: %s\n",
							src_strerror( error ) );
	}
	return static_cast<fpp_t>(m_srcData.output_frames_gen);
}

int AudioDevice::convertToS16( const surroundSampleFrame * _ab,
								const fpp_t _frames,
								const float _master_gain,
								int_sample_t * _output_buffer,
								const bool _convert_endian )
{
	if( _convert_endian )
	{
		int_sample_t temp;
		for( fpp_t frame = 0; frame < _frames; ++frame )
		{
			for( ch_cnt_t chnl = 0; chnl < channels(); ++chnl )
			{
				temp = static_cast<int_sample_t>( Mixer::clip( _ab[frame][chnl] * _master_gain ) * OUTPUT_SAMPLE_MULTIPLIER );

				( _output_buffer + frame * channels() )[chnl] =
						( temp & 0x00ff ) << 8 |
						( temp & 0xff00 ) >> 8;
			}
		}
	}
	else
	{
		for( fpp_t frame = 0; frame < _frames; ++frame )
		{
			for( ch_cnt_t chnl = 0; chnl < channels(); ++chnl )
			{
				( _output_buffer + frame * channels() )[chnl] =
						static_cast<int_sample_t>(
						Mixer::clip( _ab[frame][chnl] *
						_master_gain ) *
						OUTPUT_SAMPLE_MULTIPLIER );
			}
		}
	}

	return _frames * channels() * BYTES_PER_INT_SAMPLE;
}

void AudioDevice::clearS16Buffer( int_sample_t * _outbuf, const fpp_t _frames )
{

	assert( _outbuf != NULL );

	memset( _outbuf, 0,  _frames * channels() * BYTES_PER_INT_SAMPLE );
}

bool AudioDevice::hqAudio() const
{
	return ConfigManager::inst()->value( "mixer", "hqaudio" ).toInt();
}


code:
/*
 * AudioFileDevice.cpp - base-class for audio-device-classes which write
 *                       their output into a file
 *
 * Copyright (c) 2004-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
 *
 * This file is part of LMMS - https://lmms.io
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public
 * License along with this program (see COPYING); if not, write to the
 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301 USA.
 *
 */

#include <QMessageBox>

#include "AudioFileDevice.h"
#include "ExportProjectDialog.h"
#include "GuiApplication.h"

AudioFileDevice::AudioFileDevice( OutputSettings const & outputSettings,
					const ch_cnt_t _channels,
					const QString & _file,
					Mixer*  _mixer ) :
	AudioDevice( _channels, _mixer ),
	m_outputFile( _file ),
	m_outputSettings(outputSettings)
{
	setSampleRate( outputSettings.getSampleRate() );

	if( m_outputFile.open( QFile::WriteOnly | QFile::Truncate ) == false )
	{
		QString title, message;
		title = ExportProjectDialog::tr( "Could not open file" );
		message = ExportProjectDialog::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( _file );

		if( gui )
		{
			QMessageBox::critical( NULL, title, message,
						QMessageBox::Ok,
						QMessageBox::NoButton );
		}
		else
		{
			fprintf( stderr, "%s\n", message.toUtf8().constData() );
			exit( EXIT_FAILURE );
		}
	}
}

AudioFileDevice::~AudioFileDevice()
{
	m_outputFile.close();
}

int AudioFileDevice::writeData( const void* data, int len )
{
	if( m_outputFile.isOpen() )
	{
		return m_outputFile.write( (const char *) data, len );
	}

	return -1;
}


code:
/*
 * AudioFileFlac.cpp - Audio device which encodes a wave stream into a FLAC file (Implementation).
 *
 * Copyright (c) 2017 to present Levin Oehlmann <irrenhaus3/at/gmail[dot]com> et al.
 *
 * 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 <QtGlobal>

#include <cmath>
#include <memory>

#include "AudioFileFlac.h"
#include "endian_handling.h"
#include "Mixer.h"

AudioFileFlac::AudioFileFlac(OutputSettings const& outputSettings, ch_cnt_t const channels, bool& successful, QString const& file, Mixer* mixer):
	AudioFileDevice(outputSettings,channels,file,mixer),
	m_sf(nullptr)
{
	successful = outputFileOpened() && startEncoding();
}

AudioFileFlac::~AudioFileFlac()
{
	finishEncoding();
}

bool AudioFileFlac::startEncoding()
{
	m_sfinfo.samplerate=sampleRate();
	m_sfinfo.channels=channels();
	m_sfinfo.frames = mixer()->framesPerPeriod();
	m_sfinfo.sections=1;
	m_sfinfo.seekable=0;

	m_sfinfo.format = SF_FORMAT_FLAC;

	switch (getOutputSettings().getBitDepth())
	{
		case OutputSettings::Depth_24Bit:
		case OutputSettings::Depth_32Bit:
			// FLAC does not support 32bit sampling, so take it as 24.
			m_sfinfo.format |= SF_FORMAT_PCM_24;
			break;
		default:
			m_sfinfo.format |= SF_FORMAT_PCM_16;
	}

#ifdef LMMS_HAVE_SF_COMPLEVEL
	double compression = getOutputSettings().getCompressionLevel();
	sf_command(m_sf, SFC_SET_COMPRESSION_LEVEL, &compression, sizeof(double));
#endif

	m_sf = sf_open(
#ifdef LMMS_BUILD_WIN32
		outputFile().toLocal8Bit().constData(),
#else
		outputFile().toUtf8().constData(),
#endif
		SFM_WRITE,
		&m_sfinfo
	);

	sf_command(m_sf, SFC_SET_CLIPPING, nullptr, SF_TRUE);

	sf_set_string(m_sf, SF_STR_SOFTWARE, "LMMS");

	return true;
}

void AudioFileFlac::writeBuffer(surroundSampleFrame const* _ab, fpp_t const frames, float master_gain)
{
	OutputSettings::BitDepth depth = getOutputSettings().getBitDepth();
	float clipvalue = std::nextafterf( -1.0f, 0.0f );

	if (depth == OutputSettings::Depth_24Bit || depth == OutputSettings::Depth_32Bit) // Float encoding
	{
		std::unique_ptr<sample_t[]> buf{ new sample_t[frames*channels()] };
		for(fpp_t frame = 0; frame < frames; ++frame)
		{
			for(ch_cnt_t channel=0; channel<channels(); ++channel)
			{
				// Clip the negative side to just above -1.0 in order to prevent it from changing sign
				// Upstream issue: https://github.com/erikd/libsndfile/issues/309
				// When this commit is reverted libsndfile-1.0.29 must be made a requirement for FLAC
				buf[frame*channels() + channel] = qMax( clipvalue, _ab[frame][channel] * master_gain );
			}
		}
		sf_writef_float(m_sf,static_cast<float*>(buf.get()),frames);
	}
	else // integer PCM encoding
	{
		std::unique_ptr<int_sample_t[]> buf{ new int_sample_t[frames*channels()] };
		convertToS16(_ab, frames, master_gain, buf.get(), !isLittleEndian());
		sf_writef_short(m_sf, static_cast<short*>(buf.get()), frames);
	}

}

void AudioFileFlac::finishEncoding()
{
	if (m_sf)
	{
		sf_write_sync(m_sf);
		sf_close(m_sf);
	}
}


code:
/*
 * AudioFileMP3.cpp - Audio-device which encodes a wave stream into
 *                    an MP3 file. This is used for song export.
 *
 * Copyright (c) 2017 to present Michael Gregorius <michael.gregorius.git/at/arcor[dot]de>
 *
 * 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 "AudioFileMP3.h"

#ifdef LMMS_HAVE_MP3LAME

#include "Mixer.h"

#include <cassert>

AudioFileMP3::AudioFileMP3(	OutputSettings const & outputSettings,
				const ch_cnt_t channels,
				bool & successful,
				const QString & file,
				Mixer* mixer ) :
	AudioFileDevice( outputSettings, channels, file, mixer )
{
	successful = true;
	// For now only accept stereo sources
	successful &= channels == 2;
	successful &= initEncoder();
	successful &= outputFileOpened();
}

AudioFileMP3::~AudioFileMP3()
{
	flushRemainingBuffers();
	tearDownEncoder();
}

void AudioFileMP3::writeBuffer( const surroundSampleFrame * _buf,
					const fpp_t _frames,
					const float _master_gain )
{
	if (_frames < 1)
	{
		return;
	}

	// TODO Why isn't the gain applied by the driver but inside the device?
	std::vector<float> interleavedDataBuffer(_frames * 2);
	for (fpp_t i = 0; i < _frames; ++i)
	{
		interleavedDataBuffer[2*i] = _buf[i][0] * _master_gain;
		interleavedDataBuffer[2*i + 1] = _buf[i][1] * _master_gain;
	}

	size_t minimumBufferSize = 1.25 * _frames + 7200;
	std::vector<unsigned char> encodingBuffer(minimumBufferSize);

	int bytesWritten = lame_encode_buffer_interleaved_ieee_float(m_lame, &interleavedDataBuffer[0], _frames, &encodingBuffer[0], static_cast<int>(encodingBuffer.size()));
	assert (bytesWritten >= 0);

	writeData(&encodingBuffer[0], bytesWritten);
}

void AudioFileMP3::flushRemainingBuffers()
{
	// The documentation states that flush should have at least 7200 bytes. So let's be generous.
	std::vector<unsigned char> encodingBuffer(7200 * 4);

	int bytesWritten = lame_encode_flush(m_lame, &encodingBuffer[0], static_cast<int>(encodingBuffer.size()));
	assert (bytesWritten >= 0);

	writeData(&encodingBuffer[0], bytesWritten);
}

MPEG_mode mapToMPEG_mode(OutputSettings::StereoMode stereoMode)
{
	switch (stereoMode)
	{
	case OutputSettings::StereoMode_Stereo:
		return STEREO;
	case OutputSettings::StereoMode_JointStereo:
		return JOINT_STEREO;
	case OutputSettings::StereoMode_Mono:
		return MONO;
	default:
		return NOT_SET;
	}
}

bool AudioFileMP3::initEncoder()
{
	m_lame = lame_init();

	// Handle stereo/joint/mono settings
	OutputSettings::StereoMode stereoMode = getOutputSettings().getStereoMode();
	lame_set_mode(m_lame, mapToMPEG_mode(stereoMode));

	// Handle bit rate settings
	OutputSettings::BitRateSettings bitRateSettings = getOutputSettings().getBitRateSettings();
	int bitRate = static_cast<int>(bitRateSettings.getBitRate());

	lame_set_VBR(m_lame, vbr_off);
	lame_set_brate(m_lame, bitRate);

	// Add a comment
	id3tag_init(m_lame);
	id3tag_set_comment(m_lame, "Created with LMMS");

	return lame_init_params(m_lame) != -1;
}

void AudioFileMP3::tearDownEncoder()
{
	lame_close(m_lame);
}

#endif


code:
/*
 * AudioFileOgg.cpp - audio-device which encodes wave-stream and writes it
 *                    into an OGG-file. This is used for song-export.
 *
 * This file is based on encode.c from vorbis-tools-source, for more information
 * see below.
 *
 * Copyright (c) 2004-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
 *
 * This file is part of LMMS - https://lmms.io
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public
 * License along with this program (see COPYING); if not, write to the
 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301 USA.
 *
 */

#include "AudioFileOgg.h"

#ifdef LMMS_HAVE_OGGVORBIS

#if (QT_VERSION >= QT_VERSION_CHECK(5,10,0))
#include <QRandomGenerator>
#endif
#include <string>
#include <vorbis/vorbisenc.h>

#include "Mixer.h"

AudioFileOgg::AudioFileOgg(	OutputSettings const & outputSettings,
				const ch_cnt_t channels,
				bool & successful,
				const QString & file,
				Mixer* mixer ) :
	AudioFileDevice( outputSettings, channels, file, mixer )
{
	m_ok = successful = outputFileOpened() && startEncoding();
}

AudioFileOgg::~AudioFileOgg()
{
	finishEncoding();
}

inline int AudioFileOgg::writePage()
{
	int written = writeData( m_og.header, m_og.header_len );
	written += writeData( m_og.body, m_og.body_len );
	return written;
}

bool AudioFileOgg::startEncoding()
{
	vorbis_comment vc;
	const char * comments = "Cool=This song has been made using LMMS";
	std::string user_comments_str(comments);
	int comment_length = user_comments_str.size();
	char * user_comments = &user_comments_str[0];

	vc.user_comments = &user_comments;
	vc.comment_lengths = &comment_length;
	vc.comments = 1;
	vc.vendor = NULL;

	m_channels = channels();

	bool useVariableBitRate = getOutputSettings().getBitRateSettings().isVariableBitRate();
	bitrate_t minimalBitrate = nominalBitrate();
	bitrate_t maximumBitrate = nominalBitrate();

	if( useVariableBitRate )
	{
		minimalBitrate = minBitrate();		// min for vbr
		maximumBitrate = maxBitrate();		// max for vbr
	}

	m_rate 		= sampleRate();		// default-samplerate
	if( m_rate > 48000 )
	{
		m_rate = 48000;
		setSampleRate( 48000 );
	}

	m_comments 	= &vc;			// comments for ogg-file

	// Have vorbisenc choose a mode for us
	vorbis_info_init( &m_vi );

	if( vorbis_encode_setup_managed( &m_vi, m_channels, m_rate,
			( maximumBitrate > 0 )? maximumBitrate * 1000 : -1,
						nominalBitrate() * 1000,
			( minimalBitrate > 0 )? minimalBitrate * 1000 : -1 ) )
	{
		printf( "Mode initialization failed: invalid parameters for "
								"bitrate\n" );
		vorbis_info_clear( &m_vi );
		return false;
	}

	if( useVariableBitRate )
	{
		// Turn off management entirely (if it was turned on).
		vorbis_encode_ctl( &m_vi, OV_ECTL_RATEMANAGE_SET, NULL );
	}
	else
	{
		vorbis_encode_ctl( &m_vi, OV_ECTL_RATEMANAGE_AVG, NULL );
	}

	vorbis_encode_setup_init( &m_vi );

	// Now, set up the analysis engine, stream encoder, and other
	// preparation before the encoding begins.
	vorbis_analysis_init( &m_vd, &m_vi );
	vorbis_block_init( &m_vd, &m_vb );

	// We give our ogg file a random serial number and avoid
	// 0 and UINT32_MAX which can get you into trouble.
#if (QT_VERSION >= QT_VERSION_CHECK(5,10,0))
	// QRandomGenerator::global() is already initialized, and we can't seed() it.
	m_serialNo = 0xD0000000 + QRandomGenerator::global()->generate() % 0x0FFFFFFF;
#else
	qsrand(time(0));
	m_serialNo = 0xD0000000 + qrand() % 0x0FFFFFFF;
#endif
	ogg_stream_init( &m_os, m_serialNo );

	// Now, build the three header packets and send through to the stream
	// output stage (but defer actual file output until the main encode
	// loop)

	ogg_packet header_main;
	ogg_packet header_comments;
	ogg_packet header_codebooks;
	int result;

	// Build the packets
	vorbis_analysis_headerout( &m_vd, m_comments, &header_main,
					&header_comments, &header_codebooks );

	// And stream them out
	ogg_stream_packetin( &m_os, &header_main );
	ogg_stream_packetin( &m_os, &header_comments );
	ogg_stream_packetin( &m_os, &header_codebooks );

	while( ( result = ogg_stream_flush( &m_os, &m_og ) ) )
	{
		if( !result )
		{
			break;
		}
		int ret = writePage();
		if( ret != m_og.header_len + m_og.body_len )
		{
			// clean up
			finishEncoding();
			return false;
		}
	}

	return true;
}

void AudioFileOgg::writeBuffer( const surroundSampleFrame * _ab,
						const fpp_t _frames,
						const float _master_gain )
{
	int eos = 0;

	float * * buffer = vorbis_analysis_buffer( &m_vd, _frames *
							BYTES_PER_SAMPLE *
								channels() );
	for( fpp_t frame = 0; frame < _frames; ++frame )
	{
		for( ch_cnt_t chnl = 0; chnl < channels(); ++chnl )
		{
			buffer[chnl][frame] = _ab[frame][chnl] * _master_gain;
		}
	}

	vorbis_analysis_wrote( &m_vd, _frames );

	// While we can get enough data from the library to analyse,
	// one block at a time...
	while( vorbis_analysis_blockout( &m_vd, &m_vb ) == 1 )
	{
		// Do the main analysis, creating a packet
		vorbis_analysis( &m_vb, NULL );
		vorbis_bitrate_addblock( &m_vb );

		while( vorbis_bitrate_flushpacket( &m_vd, &m_op ) )
		{
			// Add packet to bitstream
			ogg_stream_packetin( &m_os, &m_op );

			// If we've gone over a page boundary, we can do
			// actual output, so do so (for however many pages
			// are available)
			while( !eos )
			{
				int result = ogg_stream_pageout( &m_os,
								&m_og );
				if( !result )
				{
					break;
				}

				int ret = writePage();
				if( ret != m_og.header_len +
							m_og.body_len )
				{
					printf( "failed writing to "
								"outstream\n" );
					return;
				}

				if( ogg_page_eos( &m_og ) )
				{
					eos = 1;
				}
			}
		}
	}
}

void AudioFileOgg::finishEncoding()
{
	if( m_ok )
	{
		// just for flushing buffers...
		writeBuffer( NULL, 0, 0.0f );

		// clean up
		ogg_stream_clear( &m_os );

		vorbis_block_clear( &m_vb );
		vorbis_dsp_clear( &m_vd );
		vorbis_info_clear( &m_vi );
	}
}

#endif


code:
/*
 * AudioFileWave.cpp - audio-device which encodes wave-stream and writes it
 *                     into a WAVE-file. This is used for song-export.
 *
 * Copyright (c) 2004-2013 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 "AudioFileWave.h"
#include "endian_handling.h"
#include "Mixer.h"

#include <QFile>
#include <QDebug>

AudioFileWave::AudioFileWave( OutputSettings const & outputSettings,
				const ch_cnt_t channels, bool & successful,
				const QString & file,
				Mixer* mixer ) :
	AudioFileDevice( outputSettings, channels, file, mixer ),
	m_sf( NULL )
{
	successful = outputFileOpened() && startEncoding();
}

AudioFileWave::~AudioFileWave()
{
	finishEncoding();
}

bool AudioFileWave::startEncoding()
{
	m_si.samplerate = sampleRate();
	m_si.channels = channels();
	m_si.frames = mixer()->framesPerPeriod();
	m_si.sections = 1;
	m_si.seekable = 0;

	m_si.format = SF_FORMAT_WAV;

	switch( getOutputSettings().getBitDepth() )
	{
	case OutputSettings::Depth_32Bit:
		m_si.format |= SF_FORMAT_FLOAT;
		break;
	case OutputSettings::Depth_24Bit:
		m_si.format |= SF_FORMAT_PCM_24;
		break;
	case OutputSettings::Depth_16Bit:
	default:
		m_si.format |= SF_FORMAT_PCM_16;
		break;
	}

	// Use file handle to handle unicode file name on Windows
	m_sf = sf_open_fd( outputFileHandle(), SFM_WRITE, &m_si, false );

	if (!m_sf)
	{
		qWarning("Error: AudioFileWave::startEncoding: %s", sf_strerror(nullptr));
		return false;
	}

	// Prevent fold overs when encountering clipped data
	sf_command(m_sf, SFC_SET_CLIPPING, NULL, SF_TRUE);

	sf_set_string ( m_sf, SF_STR_SOFTWARE, "LMMS" );

	return true;
}

void AudioFileWave::writeBuffer( const surroundSampleFrame * _ab,
						const fpp_t _frames,
						const float _master_gain )
{
	OutputSettings::BitDepth bitDepth = getOutputSettings().getBitDepth();

	if( bitDepth == OutputSettings::Depth_32Bit || bitDepth == OutputSettings::Depth_24Bit )
	{
		float *  buf = new float[_frames*channels()];
		for( fpp_t frame = 0; frame < _frames; ++frame )
		{
			for( ch_cnt_t chnl = 0; chnl < channels(); ++chnl )
			{
				buf[frame*channels()+chnl] = _ab[frame][chnl] *
								_master_gain;
			}
		}
		sf_writef_float( m_sf, buf, _frames );
		delete[] buf;
	}
	else
	{
		int_sample_t * buf = new int_sample_t[_frames * channels()];
		convertToS16( _ab, _frames, _master_gain, buf,
							!isLittleEndian() );

		sf_writef_short( m_sf, buf, _frames );
		delete[] buf;
	}
}

void AudioFileWave::finishEncoding()
{
	if( m_sf )
	{
		sf_close( m_sf );
	}
}


code:
/*
 * AudioJack.cpp - support for JACK transport
 *
 * 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 "AudioJack.h"

#ifdef LMMS_HAVE_JACK

#include <QLineEdit>
#include <QLabel>
#include <QMessageBox>

#include "Engine.h"
#include "GuiApplication.h"
#include "gui_templates.h"
#include "ConfigManager.h"
#include "LcdSpinBox.h"
#include "AudioPort.h"
#include "MainWindow.h"
#include "Mixer.h"
#include "MidiJack.h"

AudioJack::AudioJack( bool & _success_ful, Mixer*  _mixer ) :
	AudioDevice( qBound<int>(
		DEFAULT_CHANNELS,
		ConfigManager::inst()->value( "audiojack", "channels" ).toInt(),
		SURROUND_CHANNELS ), _mixer ),
	m_client( NULL ),
	m_active( false ),
	m_midiClient( NULL ),
	m_tempOutBufs( new jack_default_audio_sample_t *[channels()] ),
	m_outBuf( new surroundSampleFrame[mixer()->framesPerPeriod()] ),
	m_framesDoneInCurBuf( 0 ),
	m_framesToDoInCurBuf( 0 )
{
	m_stopped = true;

	_success_ful = initJackClient();
	if( _success_ful )
	{
		connect( this, SIGNAL( zombified() ),
				this, SLOT( restartAfterZombified() ),
				Qt::QueuedConnection );
	}

}

AudioJack::~AudioJack()
{
	stopProcessing();
#ifdef AUDIO_PORT_SUPPORT
	while( m_portMap.size() )
	{
		unregisterPort( m_portMap.begin().key() );
	}
#endif

	if( m_client != NULL )
	{
		if( m_active )
		{
			jack_deactivate( m_client );
		}
		jack_client_close( m_client );
	}

	delete[] m_tempOutBufs;

	delete[] m_outBuf;
}

void AudioJack::restartAfterZombified()
{
	if( initJackClient() )
	{
		m_active = false;
		startProcessing();
		QMessageBox::information( gui->mainWindow(),
			tr( "JACK client restarted" ),
			tr( "LMMS was kicked by JACK for some reason. "
				"Therefore the JACK backend of LMMS has been "
				"restarted. You will have to make manual "
				"connections again." ) );
	}
	else
	{
		QMessageBox::information( gui->mainWindow(),
			tr( "JACK server down" ),
			tr( "The JACK server seems to have been shutdown "
				"and starting a new instance failed. "
				"Therefore LMMS is unable to proceed. "
				"You should save your project and restart "
						"JACK and LMMS." ) );
	}
}

AudioJack* AudioJack::addMidiClient(MidiJack *midiClient)
{
	if( m_client == NULL )
		return NULL;

	m_midiClient = midiClient;

	return this;
}

bool AudioJack::initJackClient()
{
	QString clientName = ConfigManager::inst()->value( "audiojack",
								"clientname" );
	if( clientName.isEmpty() )
	{
		clientName = "lmms";
	}

	const char * serverName = NULL;
	jack_status_t status;
	m_client = jack_client_open( clientName.toLatin1().constData(),
						JackNullOption, &status,
								serverName );
	if( m_client == NULL )
	{
		printf( "jack_client_open() failed, status 0x%2.0x\n", status );
		if( status & JackServerFailed )
		{
			printf( "Could not connect to JACK server.\n" );
		}
		return false;
	}
	if( status & JackNameNotUnique )
	{
		printf( "there's already a client with name '%s', so unique "
			"name '%s' was assigned\n", clientName.
							toLatin1().constData(),
					jack_get_client_name( m_client ) );
	}

	// set process-callback
	jack_set_process_callback( m_client, staticProcessCallback, this );

	// set shutdown-callback
	jack_on_shutdown( m_client, shutdownCallback, this );

	if( jack_get_sample_rate( m_client ) != sampleRate() )
	{
		setSampleRate( jack_get_sample_rate( m_client ) );
	}

	for( ch_cnt_t ch = 0; ch < channels(); ++ch )
	{
		QString name = QString( "master out " ) +
				( ( ch % 2 ) ? "R" : "L" ) +
				QString::number( ch / 2 + 1 );
		m_outputPorts.push_back( jack_port_register( m_client,
						name.toLatin1().constData(),
						JACK_DEFAULT_AUDIO_TYPE,
						JackPortIsOutput, 0 ) );
		if( m_outputPorts.back() == NULL )
		{
			printf( "no more JACK-ports available!\n" );
			return false;
		}
	}

	return true;
}

void AudioJack::startProcessing()
{
	if( m_active || m_client == NULL )
	{
		m_stopped = false;
		return;
	}

	if( jack_activate( m_client ) )
	{
		printf( "cannot activate client\n" );
		return;
	}

	m_active = true;

	// try to sync JACK's and LMMS's buffer-size
//	jack_set_buffer_size( m_client, mixer()->framesPerPeriod() );

	const char * * ports = jack_get_ports( m_client, NULL, NULL,
						JackPortIsPhysical |
						JackPortIsInput );
	if( ports == NULL )
	{
		printf( "no physical playback ports. you'll have to do "
			"connections at your own!\n" );
	}
	else
	{
		for( ch_cnt_t ch = 0; ch < channels(); ++ch )
		{
			if( jack_connect( m_client, jack_port_name(
							m_outputPorts[ch] ),
								ports[ch] ) )
			{
				printf( "cannot connect output ports. you'll "
					"have to do connections at your own!\n"
									);
			}
		}
	}

	m_stopped = false;
	free( ports );
}

void AudioJack::stopProcessing()
{
	m_stopped = true;
}

void AudioJack::applyQualitySettings()
{
	if( hqAudio() )
	{
		setSampleRate( Engine::mixer()->processingSampleRate() );

		if( jack_get_sample_rate( m_client ) != sampleRate() )
		{
			setSampleRate( jack_get_sample_rate( m_client ) );
		}
	}

	AudioDevice::applyQualitySettings();
}

void AudioJack::registerPort( AudioPort * _port )
{
#ifdef AUDIO_PORT_SUPPORT
	// make sure, port is not already registered
	unregisterPort( _port );
	const QString name[2] = { _port->name() + " L",
					_port->name() + " R" } ;

	for( ch_cnt_t ch = 0; ch < DEFAULT_CHANNELS; ++ch )
	{
		m_portMap[_port].ports[ch] = jack_port_register( m_client,
						name[ch].toLatin1().constData(),
						JACK_DEFAULT_AUDIO_TYPE,
							JackPortIsOutput, 0 );
	}
#endif
}

void AudioJack::unregisterPort( AudioPort * _port )
{
#ifdef AUDIO_PORT_SUPPORT
	if( m_portMap.contains( _port ) )
	{
		for( ch_cnt_t ch = 0; ch < DEFAULT_CHANNELS; ++ch )
		{
			if( m_portMap[_port].ports[ch] != NULL )
			{
				jack_port_unregister( m_client,
						m_portMap[_port].ports[ch] );
			}
		}
		m_portMap.erase( m_portMap.find( _port ) );
	}
#endif
}

void AudioJack::renamePort( AudioPort * _port )
{
#ifdef AUDIO_PORT_SUPPORT
	if( m_portMap.contains( _port ) )
	{
		const QString name[2] = { _port->name() + " L",
					_port->name() + " R" };
		for( ch_cnt_t ch = 0; ch < DEFAULT_CHANNELS; ++ch )
		{
#ifdef LMMS_HAVE_JACK_PRENAME
			jack_port_rename( m_client, m_portMap[_port].ports[ch],
					name[ch].toLatin1().constData() );
#else
			jack_port_set_name( m_portMap[_port].ports[ch],
					name[ch].toLatin1().constData() );
#endif
		}
	}
#endif
}

int AudioJack::processCallback( jack_nframes_t _nframes, void * _udata )
{

	// do midi processing first so that midi input can
	// add to the following sound processing
	if( m_midiClient && _nframes > 0 )
	{
		m_midiClient.load()->JackMidiRead(_nframes);
		m_midiClient.load()->JackMidiWrite(_nframes);
	}

	for( int c = 0; c < channels(); ++c )
	{
		m_tempOutBufs[c] =
			(jack_default_audio_sample_t *) jack_port_get_buffer(
												m_outputPorts[c], _nframes );
	}

#ifdef AUDIO_PORT_SUPPORT
	const int frames = qMin<int>( _nframes, mixer()->framesPerPeriod() );
	for( JackPortMap::iterator it = m_portMap.begin();
						it != m_portMap.end(); ++it )
	{
		for( ch_cnt_t ch = 0; ch < channels(); ++ch )
		{
			if( it.value().ports[ch] == NULL )
			{
				continue;
			}
			jack_default_audio_sample_t * buf =
			(jack_default_audio_sample_t *) jack_port_get_buffer(
							it.value().ports[ch],
								_nframes );
			for( int frame = 0; frame < frames; ++frame )
			{
				buf[frame] = it.key()->buffer()[frame][ch];
			}
		}
	}
#endif

	jack_nframes_t done = 0;
	while( done < _nframes && m_stopped == false )
	{
		jack_nframes_t todo = qMin<jack_nframes_t>(
						_nframes,
						m_framesToDoInCurBuf -
							m_framesDoneInCurBuf );
		const float gain = mixer()->masterGain();
		for( int c = 0; c < channels(); ++c )
		{
			jack_default_audio_sample_t * o = m_tempOutBufs[c];
			for( jack_nframes_t frame = 0; frame < todo; ++frame )
			{
				o[done+frame] = m_outBuf[m_framesDoneInCurBuf+frame][c] * gain;
			}
		}
		done += todo;
		m_framesDoneInCurBuf += todo;
		if( m_framesDoneInCurBuf == m_framesToDoInCurBuf )
		{
			m_framesToDoInCurBuf = getNextBuffer( m_outBuf );
			m_framesDoneInCurBuf = 0;
			if( !m_framesToDoInCurBuf )
			{
				m_stopped = true;
				break;
			}
		}
	}

	if( _nframes != done )
	{
		for( int c = 0; c < channels(); ++c )
		{
			jack_default_audio_sample_t * b = m_tempOutBufs[c] + done;
			memset( b, 0, sizeof( *b ) * ( _nframes - done ) );
		}
	}

	return 0;
}

int AudioJack::staticProcessCallback( jack_nframes_t _nframes, void * _udata )
{
	return static_cast<AudioJack *>( _udata )->
					processCallback( _nframes, _udata );
}

void AudioJack::shutdownCallback( void * _udata )
{
	AudioJack * _this = static_cast<AudioJack *>( _udata );
	_this->m_client = NULL;
	_this->zombified();
}

AudioJack::setupWidget::setupWidget( QWidget * _parent ) :
	AudioDeviceSetupWidget( AudioJack::name(), _parent )
{
	QString cn = ConfigManager::inst()->value( "audiojack", "clientname" );
	if( cn.isEmpty() )
	{
		cn = "lmms";
	}
	m_clientName = new QLineEdit( cn, this );
	m_clientName->setGeometry( 10, 20, 160, 20 );

	QLabel * cn_lbl = new QLabel( tr( "Client name" ), this );
	cn_lbl->setFont( pointSize<7>( cn_lbl->font() ) );
	cn_lbl->setGeometry( 10, 40, 160, 10 );

	LcdSpinBoxModel * m = new LcdSpinBoxModel( /* this */ );
	m->setRange( DEFAULT_CHANNELS, SURROUND_CHANNELS );
	m->setStep( 2 );
	m->setValue( ConfigManager::inst()->value( "audiojack",
							"channels" ).toInt() );

	m_channels = new LcdSpinBox( 1, this );
	m_channels->setModel( m );
	m_channels->setLabel( tr( "Channels" ) );
	m_channels->move( 180, 20 );

}

AudioJack::setupWidget::~setupWidget()
{
	delete m_channels->model();
}

void AudioJack::setupWidget::saveSettings()
{
	ConfigManager::inst()->setValue( "audiojack", "clientname",
							m_clientName->text() );
	ConfigManager::inst()->setValue( "audiojack", "channels",
				QString::number( m_channels->value<int>() ) );
}

#endif


code:
/*
 * AudioOss.cpp - device-class that implements OSS-PCM-output
 *
 * Copyright (c) 2004-2009 Tobias Doerffel <tobydox/at/users.sourceforge.net>
 *
 * This file is part of LMMS - https://lmms.io
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public
 * License along with this program (see COPYING); if not, write to the
 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301 USA.
 *
 */

#include "AudioOss.h"

#ifdef LMMS_HAVE_OSS

#include <QFileInfo>
#include <QLabel>
#include <QLineEdit>

#include "endian_handling.h"
#include "LcdSpinBox.h"
#include "Mixer.h"
#include "Engine.h"
#include "gui_templates.h"

#ifdef LMMS_HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef LMMS_HAVE_FCNTL_H
#include <fcntl.h>
#endif
#ifdef LMMS_HAVE_SYS_IOCTL_H
#include <sys/ioctl.h>
#endif

#ifdef LMMS_HAVE_SYS_SOUNDCARD_H
// This is recommended by OSS
#include <sys/soundcard.h>
#elif defined(LMMS_HAVE_SOUNDCARD_H)
// This is installed on some systems
#include <soundcard.h>
#endif

#include "ConfigManager.h"

#ifndef _PATH_DEV_DSP
#if defined(__NetBSD__) || defined(__OpenBSD__)
#define _PATH_DEV_DSP  "/dev/audio"
#else
#define _PATH_DEV_DSP  "/dev/dsp"
#endif
#endif

AudioOss::AudioOss( bool & _success_ful, Mixer*  _mixer ) :
	AudioDevice( qBound<ch_cnt_t>(
		DEFAULT_CHANNELS,
		ConfigManager::inst()->value( "audiooss", "channels" ).toInt(),
		SURROUND_CHANNELS ), _mixer ),
	m_convertEndian( false )
{
	_success_ful = false;

	m_audioFD = open( probeDevice().toLatin1().constData(), O_WRONLY, 0 );

	if( m_audioFD == -1 )
	{
		printf( "AudioOss: failed opening audio-device\n" );
		return;
	}

	// Make the file descriptor use blocking writes with fcntl()
	if ( fcntl( m_audioFD, F_SETFL, fcntl( m_audioFD, F_GETFL ) &
							~O_NONBLOCK ) < 0 )
	{
		printf( "could not set audio blocking mode\n" );
		return;
	}

	// set FD_CLOEXEC flag for file descriptor so forked processes
	// do not inherit it
	fcntl( m_audioFD, F_SETFD, fcntl( m_audioFD, F_GETFD ) | FD_CLOEXEC );

	int frag_spec;
	for( frag_spec = 0; static_cast<int>( 0x01 << frag_spec ) <
		mixer()->framesPerPeriod() * channels() *
							BYTES_PER_INT_SAMPLE;
		++frag_spec )
	{
	}

	frag_spec |= 0x00020000;	// two fragments, for low latency

	if ( ioctl( m_audioFD, SNDCTL_DSP_SETFRAGMENT, &frag_spec ) < 0 )
	{
		perror( "SNDCTL_DSP_SETFRAGMENT" );
		printf( "Warning: Couldn't set audio fragment size\n" );
	}

	unsigned int value;
	// Get a list of supported hardware formats
	if ( ioctl( m_audioFD, SNDCTL_DSP_GETFMTS, &value ) < 0 )
	{
		perror( "SNDCTL_DSP_GETFMTS" );
		printf( "Couldn't get audio format list\n" );
		return;
	}

	// Set the audio format
	if( value & AFMT_S16_LE )
	{
		value = AFMT_S16_LE;
	}
	else if( value & AFMT_S16_BE )
	{
		value = AFMT_S16_BE;
	}
	else
	{
		printf(" Soundcard doesn't support signed 16-bit-data\n");
	}
	if ( ioctl( m_audioFD, SNDCTL_DSP_SETFMT, &value ) < 0 )
	{
		perror( "SNDCTL_DSP_SETFMT" );
		printf( "Couldn't set audio format\n" );
		return;
	}
	if( ( isLittleEndian() && ( value == AFMT_S16_BE ) ) ||
			( !isLittleEndian() && ( value == AFMT_S16_LE ) ) )
	{
		m_convertEndian = true;
	}

	// Set the number of channels of output
	value = channels();
	if ( ioctl( m_audioFD, SNDCTL_DSP_CHANNELS, &value ) < 0 )
	{
		perror( "SNDCTL_DSP_CHANNELS" );
		printf( "Cannot set the number of channels\n" );
		return;
	}
	if( value != channels() )
	{
		printf( "Couldn't set number of channels\n" );
		return;
	}

	// Set the DSP frequency
	value = sampleRate();
	if ( ioctl( m_audioFD, SNDCTL_DSP_SPEED, &value ) < 0 )
	{
		perror( "SNDCTL_DSP_SPEED" );
		printf( "Couldn't set audio frequency\n" );
		return;
	}
	if( value != sampleRate() )
	{
		value = mixer()->baseSampleRate();
		if ( ioctl( m_audioFD, SNDCTL_DSP_SPEED, &value ) < 0 )
		{
			perror( "SNDCTL_DSP_SPEED" );
			printf( "Couldn't set audio frequency\n" );
			return;
		}
		setSampleRate( value );
	}

	_success_ful = true;
}

AudioOss::~AudioOss()
{
	stopProcessing();
	close( m_audioFD );
}

QString AudioOss::probeDevice()
{
	QString dev = ConfigManager::inst()->value( "AudioOss", "Device" );
	if( dev.isEmpty() )
	{
		char * adev = getenv( "AUDIODEV" );	// Is there a standard
							// variable name?
		if( adev != NULL )
		{
			dev = adev;
		}
		else
		{
			dev = _PATH_DEV_DSP;		// default device
		}
	}

	// if the first open fails, look for other devices
	if( QFileInfo( dev ).isWritable() == false )
	{
		int instance = -1;
		while( 1 )
		{
			dev = _PATH_DEV_DSP + QString::number( ++instance );
			if( !QFileInfo( dev ).exists() )
			{
				dev = _PATH_DEV_DSP;
				break;
			}
			if( QFileInfo( dev ).isWritable() )
			{
				break;
			}
		}
	}
	return dev;
}

void AudioOss::startProcessing()
{
	if( !isRunning() )
	{
		start( QThread::HighPriority );
	}
}

void AudioOss::stopProcessing()
{
	stopProcessingThread( this );
}

void AudioOss::applyQualitySettings()
{
	if( hqAudio() )
	{
		setSampleRate( Engine::mixer()->processingSampleRate() );

		unsigned int value = sampleRate();
		if ( ioctl( m_audioFD, SNDCTL_DSP_SPEED, &value ) < 0 )
		{
			perror( "SNDCTL_DSP_SPEED" );
			printf( "Couldn't set audio frequency\n" );
			return;
		}
		if( value != sampleRate() )
		{
			value = mixer()->baseSampleRate();
			if ( ioctl( m_audioFD, SNDCTL_DSP_SPEED, &value ) < 0 )
			{
				perror( "SNDCTL_DSP_SPEED" );
				printf( "Couldn't set audio frequency\n" );
				return;
			}
			setSampleRate( value );
		}
	}

	AudioDevice::applyQualitySettings();
}

void AudioOss::run()
{
	surroundSampleFrame * temp =
		new surroundSampleFrame[mixer()->framesPerPeriod()];
	int_sample_t * outbuf =
			new int_sample_t[mixer()->framesPerPeriod() *
								channels()];

	while( true )
	{
		const fpp_t frames = getNextBuffer( temp );
		if( !frames )
		{
			break;
		}

		int bytes = convertToS16( temp, frames,
				mixer()->masterGain(), outbuf,
							m_convertEndian );
		if( write( m_audioFD, outbuf, bytes ) != bytes )
		{
			break;
		}
	}

	delete[] temp;
	delete[] outbuf;
}

AudioOss::setupWidget::setupWidget( QWidget * _parent ) :
	AudioDeviceSetupWidget( AudioOss::name(), _parent )
{
	m_device = new QLineEdit( probeDevice(), this );
	m_device->setGeometry( 10, 20, 160, 20 );

	QLabel * dev_lbl = new QLabel( tr( "Device" ), this );
	dev_lbl->setFont( pointSize<7>( dev_lbl->font() ) );
	dev_lbl->setGeometry( 10, 40, 160, 10 );

	LcdSpinBoxModel * m = new LcdSpinBoxModel( /* this */ );
	m->setRange( DEFAULT_CHANNELS, SURROUND_CHANNELS );
	m->setStep( 2 );
	m->setValue( ConfigManager::inst()->value( "audiooss",
							"channels" ).toInt() );

	m_channels = new LcdSpinBox( 1, this );
	m_channels->setModel( m );
	m_channels->setLabel( tr( "Channels" ) );
	m_channels->move( 180, 20 );

}

AudioOss::setupWidget::~setupWidget()
{
	delete m_channels->model();
}

void AudioOss::setupWidget::saveSettings()
{
	ConfigManager::inst()->setValue( "audiooss", "device",
							m_device->text() );
	ConfigManager::inst()->setValue( "audiooss", "channels",
				QString::number( m_channels->value<int>() ) );
}

#endif