code:
/*
 * NotePlayHandle.cpp - implementation of class NotePlayHandle which manages
 *                      playback of a single note by an instrument
 *
 * 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 "NotePlayHandle.h"
#include "BasicFilters.h"
#include "DetuningHelper.h"
#include "InstrumentSoundShaping.h"
#include "InstrumentTrack.h"
#include "Instrument.h"
#include "Mixer.h"
#include "Song.h"

NotePlayHandle::BaseDetuning::BaseDetuning( DetuningHelper *detuning ) :
	m_value( detuning ? detuning->automationPattern()->valueAt( 0 ) : 0 )
{
}

NotePlayHandle::NotePlayHandle( InstrumentTrack* instrumentTrack,
								const f_cnt_t _offset,
								const f_cnt_t _frames,
								const Note& n,
								NotePlayHandle *parent,
								int midiEventChannel,
								Origin origin ) :
	PlayHandle( TypeNotePlayHandle, _offset ),
	Note( n.length(), n.pos(), n.key(), n.getVolume(), n.getPanning(), n.detuning() ),
	m_pluginData( NULL ),
	m_instrumentTrack( instrumentTrack ),
	m_frames( 0 ),
	m_totalFramesPlayed( 0 ),
	m_framesBeforeRelease( 0 ),
	m_releaseFramesToDo( 0 ),
	m_releaseFramesDone( 0 ),
	m_subNotes(),
	m_released( false ),
	m_releaseStarted( false ),
	m_hasMidiNote( false ),
	m_hasParent( parent != NULL  ),
	m_parent( parent ),
	m_hadChildren( false ),
	m_muted( false ),
	m_bbTrack( NULL ),
	m_origTempo( Engine::getSong()->getTempo() ),
	m_origBaseNote( instrumentTrack->baseNote() ),
	m_frequency( 0 ),
	m_unpitchedFrequency( 0 ),
	m_baseDetuning( NULL ),
	m_songGlobalParentOffset( 0 ),
	m_midiChannel( midiEventChannel >= 0 ? midiEventChannel : instrumentTrack->midiPort()->realOutputChannel() ),
	m_origin( origin ),
	m_frequencyNeedsUpdate( false )
{
	lock();
	if( hasParent() == false )
	{
		m_baseDetuning = new BaseDetuning( detuning() );
		m_instrumentTrack->m_processHandles.push_back( this );
	}
	else
	{
		m_baseDetuning = parent->m_baseDetuning;

		parent->m_subNotes.push_back( this );
		parent->m_hadChildren = true;

		m_bbTrack = parent->m_bbTrack;

		parent->setUsesBuffer( false );
	}

	updateFrequency();

	setFrames( _frames );

	// inform attached components about new MIDI note (used for recording in Piano Roll)
	if( m_origin == OriginMidiInput )
	{
		m_instrumentTrack->midiNoteOn( *this );
	}

	if( m_instrumentTrack->instrument()->flags() & Instrument::IsSingleStreamed )
	{
		setUsesBuffer( false );
	}

	setAudioPort( instrumentTrack->audioPort() );

	unlock();
}

NotePlayHandle::~NotePlayHandle()
{
	lock();
	noteOff( 0 );

	if( hasParent() == false )
	{
		delete m_baseDetuning;
		m_instrumentTrack->m_processHandles.removeAll( this );
	}
	else
	{
		m_parent->m_subNotes.removeOne( this );
	}

	if( m_pluginData != NULL )
	{
		m_instrumentTrack->deleteNotePluginData( this );
	}

	if( m_instrumentTrack->m_notes[key()] == this )
	{
		m_instrumentTrack->m_notes[key()] = NULL;
	}

	m_subNotes.clear();

	if( buffer() ) releaseBuffer();

	unlock();
}

void NotePlayHandle::setVolume( volume_t _volume )
{
	Note::setVolume( _volume );

	const int baseVelocity = m_instrumentTrack->midiPort()->baseVelocity();

	m_instrumentTrack->processOutEvent( MidiEvent( MidiKeyPressure, midiChannel(), midiKey(), midiVelocity( baseVelocity ) ) );
}

void NotePlayHandle::setPanning( panning_t panning )
{
	Note::setPanning( panning );
}

int NotePlayHandle::midiKey() const
{
	return key() - m_origBaseNote + instrumentTrack()->baseNote();
}

void NotePlayHandle::play( sampleFrame * _working_buffer )
{
	if( m_muted )
	{
		return;
	}

	// if the note offset falls over to next period, then don't start playback yet
	if( offset() >= Engine::mixer()->framesPerPeriod() )
	{
		setOffset( offset() - Engine::mixer()->framesPerPeriod() );
		return;
	}

	lock();

	/* It is possible for NotePlayHandle::noteOff to be called before NotePlayHandle::play,
	 * which results in a note-on message being sent without a subsequent note-off message.
	 * Therefore, we check here whether the note has already been released before sending
	 * the note-on message. */
	if( !m_released
		&& m_totalFramesPlayed == 0 && !m_hasMidiNote
		&& ( hasParent() || ! m_instrumentTrack->isArpeggioEnabled() ) )
	{
		m_hasMidiNote = true;

		const int baseVelocity = m_instrumentTrack->midiPort()->baseVelocity();

		// send MidiNoteOn event
		m_instrumentTrack->processOutEvent(
			MidiEvent( MidiNoteOn, midiChannel(), midiKey(), midiVelocity( baseVelocity ) ),
			TimePos::fromFrames( offset(), Engine::framesPerTick() ),
			offset() );
	}

	if( m_frequencyNeedsUpdate )
	{
		updateFrequency();
	}

	// number of frames that can be played this period
	f_cnt_t framesThisPeriod = m_totalFramesPlayed == 0
		? Engine::mixer()->framesPerPeriod() - offset()
		: Engine::mixer()->framesPerPeriod();

	// check if we start release during this period
	if( m_released == false &&
		instrumentTrack()->isSustainPedalPressed() == false &&
		m_totalFramesPlayed + framesThisPeriod > m_frames )
	{
		noteOff( m_totalFramesPlayed == 0
			? ( m_frames + offset() ) // if we have noteon and noteoff during the same period, take offset in account for release frame
			: ( m_frames - m_totalFramesPlayed ) ); // otherwise, the offset is already negated and can be ignored
	}

	// under some circumstances we're called even if there's nothing to play
	// therefore do an additional check which fixes crash e.g. when
	// decreasing release of an instrument-track while the note is active
	if( framesLeft() > 0 )
	{
		// play note!
		m_instrumentTrack->playNote( this, _working_buffer );
	}

	if( m_released && (!instrumentTrack()->isSustainPedalPressed() ||
		m_releaseStarted) )
	{
		m_releaseStarted = true;

		f_cnt_t todo = framesThisPeriod;

		// if this note is base-note for arpeggio, always set
		// m_releaseFramesToDo to bigger value than m_releaseFramesDone
		// because we do not allow NotePlayHandle::isFinished() to be true
		// until all sub-notes are completely played and no new ones
		// are inserted by arpAndChordsTabWidget::processNote()
		if( ! m_subNotes.isEmpty() )
		{
			m_releaseFramesToDo = m_releaseFramesDone + 2 * Engine::mixer()->framesPerPeriod();
		}
		// look whether we have frames left to be done before release
		if( m_framesBeforeRelease )
		{
			// yes, then look whether these samples can be played
			// within one audio-buffer
			if( m_framesBeforeRelease <= framesThisPeriod )
			{
				// yes, then we did less releaseFramesDone
				todo -= m_framesBeforeRelease;
				m_framesBeforeRelease = 0;
			}
			else
			{
				// no, then just decrease framesBeforeRelease
				// and wait for next loop... (we're not in
				// release-phase yet)
				todo = 0;
				m_framesBeforeRelease -= framesThisPeriod;
			}
		}
		// look whether we're in release-phase
		if( todo && m_releaseFramesDone < m_releaseFramesToDo )
		{
			// check whether we have to do more frames in current
			// loop than left in current loop
			if( m_releaseFramesToDo - m_releaseFramesDone >= todo )
			{
				// yes, then increase number of release-frames
				// done
				m_releaseFramesDone += todo;
			}
			else
			{
				// no, we did all in this loop!
				m_releaseFramesDone = m_releaseFramesToDo;
			}
		}
	}

	// update internal data
	m_totalFramesPlayed += framesThisPeriod;
	unlock();
}

f_cnt_t NotePlayHandle::framesLeft() const
{
	if( instrumentTrack()->isSustainPedalPressed() )
	{
		return 4*Engine::mixer()->framesPerPeriod();
	}
	else if( m_released && actualReleaseFramesToDo() == 0 )
	{
		return m_framesBeforeRelease;
	}
	else if( m_released )
	{
		return m_framesBeforeRelease + m_releaseFramesToDo - m_releaseFramesDone;
	}
	return m_frames+actualReleaseFramesToDo()-m_totalFramesPlayed;
}

fpp_t NotePlayHandle::framesLeftForCurrentPeriod() const
{
	if( m_totalFramesPlayed == 0 )
	{
		return (fpp_t) qMin<f_cnt_t>( framesLeft(), Engine::mixer()->framesPerPeriod() - offset() );
	}
	return (fpp_t) qMin<f_cnt_t>( framesLeft(), Engine::mixer()->framesPerPeriod() );
}

bool NotePlayHandle::isFromTrack( const Track * _track ) const
{
	return m_instrumentTrack == _track || m_bbTrack == _track;
}

void NotePlayHandle::noteOff( const f_cnt_t _s )
{
	if( m_released )
	{
		return;
	}
	m_released = true;

	// first note-off all sub-notes
	for( NotePlayHandle * n : m_subNotes )
	{
		n->lock();
		n->noteOff( _s );
		n->unlock();
	}

	// then set some variables indicating release-state
	m_framesBeforeRelease = _s;
	m_releaseFramesToDo = qMax<f_cnt_t>( 0, actualReleaseFramesToDo() );

	if( m_hasMidiNote )
	{
		m_hasMidiNote = false;

		// send MidiNoteOff event
		m_instrumentTrack->processOutEvent(
				MidiEvent( MidiNoteOff, midiChannel(), midiKey(), 0 ),
				TimePos::fromFrames( _s, Engine::framesPerTick() ),
				_s );
	}

	// inform attached components about MIDI finished (used for recording in Piano Roll)
	if (!instrumentTrack()->isSustainPedalPressed())
	{
		if( m_origin == OriginMidiInput )
		{
			setLength( TimePos( static_cast<f_cnt_t>( totalFramesPlayed() / Engine::framesPerTick() ) ) );
			m_instrumentTrack->midiNoteOff( *this );
		}
	}
}

f_cnt_t NotePlayHandle::actualReleaseFramesToDo() const
{
	return m_instrumentTrack->m_soundShaping.releaseFrames();
}

void NotePlayHandle::setFrames( const f_cnt_t _frames )
{
	m_frames = _frames;
	if( m_frames == 0 )
	{
		m_frames = m_instrumentTrack->beatLen( this );
	}
	m_origFrames = m_frames;
}

float NotePlayHandle::volumeLevel( const f_cnt_t _frame )
{
	return m_instrumentTrack->m_soundShaping.volumeLevel( this, _frame );
}

void NotePlayHandle::mute()
{
	// mute all sub-notes
	for( NotePlayHandleList::Iterator it = m_subNotes.begin(); it != m_subNotes.end(); ++it )
	{
		( *it )->mute();
	}
	m_muted = true;
}

int NotePlayHandle::index() const
{
	const PlayHandleList & playHandles = Engine::mixer()->playHandles();
	int idx = 0;
	for( PlayHandleList::ConstIterator it = playHandles.begin(); it != playHandles.end(); ++it )
	{
		const NotePlayHandle * nph = dynamic_cast<const NotePlayHandle *>( *it );
		if( nph == NULL || nph->m_instrumentTrack != m_instrumentTrack || nph->isReleased() || nph->hasParent() )
		{
			continue;
		}
		if( nph == this )
		{
			return idx;
		}
		++idx;
	}
	return -1;
}

ConstNotePlayHandleList NotePlayHandle::nphsOfInstrumentTrack( const InstrumentTrack * _it, bool _all_ph )
{
	const PlayHandleList & playHandles = Engine::mixer()->playHandles();
	ConstNotePlayHandleList cnphv;

	for( PlayHandleList::ConstIterator it = playHandles.begin(); it != playHandles.end(); ++it )
	{
		const NotePlayHandle * nph = dynamic_cast<const NotePlayHandle *>( *it );
		if( nph != NULL && nph->m_instrumentTrack == _it && ( ( nph->isReleased() == false && nph->hasParent() == false ) || _all_ph == true ) )
		{
			cnphv.push_back( nph );
		}
	}
	return cnphv;
}

bool NotePlayHandle::operator==( const NotePlayHandle & _nph ) const
{
	return length() == _nph.length() &&
			pos() == _nph.pos() &&
			key() == _nph.key() &&
			getVolume() == _nph.getVolume() &&
			getPanning() == _nph.getPanning() &&
			m_instrumentTrack == _nph.m_instrumentTrack &&
			m_frames == _nph.m_frames &&
			offset() == _nph.offset() &&
			m_totalFramesPlayed == _nph.m_totalFramesPlayed &&
			m_released == _nph.m_released &&
			m_hasParent == _nph.m_hasParent &&
			m_origBaseNote == _nph.m_origBaseNote &&
			m_muted == _nph.m_muted &&
			m_midiChannel == _nph.m_midiChannel &&
			m_origin == _nph.m_origin;
}

void NotePlayHandle::updateFrequency()
{
	int mp = m_instrumentTrack->m_useMasterPitchModel.value() ? Engine::getSong()->masterPitch() : 0;
	const float pitch =
		( key() -
				m_instrumentTrack->baseNoteModel()->value() +
				mp +
				m_baseDetuning->value() )
												 / 12.0f;
	m_frequency = BaseFreq * powf( 2.0f, pitch + m_instrumentTrack->pitchModel()->value() / ( 100 * 12.0f ) );
	m_unpitchedFrequency = BaseFreq * powf( 2.0f, pitch );

	for( NotePlayHandleList::Iterator it = m_subNotes.begin(); it != m_subNotes.end(); ++it )
	{
		( *it )->updateFrequency();
	}
}

void NotePlayHandle::processTimePos( const TimePos& time )
{
	if( detuning() && time >= songGlobalParentOffset()+pos() )
	{
		const float v = detuning()->automationPattern()->valueAt( time - songGlobalParentOffset() - pos() );
		if( !typeInfo<float>::isEqual( v, m_baseDetuning->value() ) )
		{
			m_baseDetuning->setValue( v );
			updateFrequency();
		}
	}
}

void NotePlayHandle::resize( const bpm_t _new_tempo )
{
	if (origin() == OriginMidiInput ||
		(origin() == OriginNoteStacking && m_parent->origin() == OriginMidiInput))
	{
		// Don't resize notes from MIDI input - they should continue to play
		// until the key is released, and their large duration can cause
		// overflows in this method.
		return;
	}

	double completed = m_totalFramesPlayed / (double) m_frames;
	double new_frames = m_origFrames * m_origTempo / (double) _new_tempo;
	m_frames = (f_cnt_t)new_frames;
	m_totalFramesPlayed = (f_cnt_t)( completed * new_frames );

	for( NotePlayHandleList::Iterator it = m_subNotes.begin(); it != m_subNotes.end(); ++it )
	{
		( *it )->resize( _new_tempo );
	}
}

NotePlayHandle ** NotePlayHandleManager::s_available;
QReadWriteLock NotePlayHandleManager::s_mutex;
std::atomic_int NotePlayHandleManager::s_availableIndex;
int NotePlayHandleManager::s_size;

void NotePlayHandleManager::init()
{
	s_available = MM_ALLOC( NotePlayHandle*, INITIAL_NPH_CACHE );

	NotePlayHandle * n = MM_ALLOC( NotePlayHandle, INITIAL_NPH_CACHE );

	for( int i=0; i < INITIAL_NPH_CACHE; ++i )
	{
		s_available[ i ] = n;
		++n;
	}
	s_availableIndex = INITIAL_NPH_CACHE - 1;
	s_size = INITIAL_NPH_CACHE;
}

NotePlayHandle * NotePlayHandleManager::acquire( InstrumentTrack* instrumentTrack,
				const f_cnt_t offset,
				const f_cnt_t frames,
				const Note& noteToPlay,
				NotePlayHandle* parent,
				int midiEventChannel,
				NotePlayHandle::Origin origin )
{
	// TODO: use some lockless data structures
	s_mutex.lockForWrite();
	if (s_availableIndex < 0) { extend(NPH_CACHE_INCREMENT); }
	NotePlayHandle * nph = s_available[s_availableIndex--];
	s_mutex.unlock();

	new( (void*)nph ) NotePlayHandle( instrumentTrack, offset, frames, noteToPlay, parent, midiEventChannel, origin );
	return nph;
}

void NotePlayHandleManager::release( NotePlayHandle * nph )
{
	nph->NotePlayHandle::~NotePlayHandle();
	s_mutex.lockForRead();
	s_available[++s_availableIndex] = nph;
	s_mutex.unlock();
}

void NotePlayHandleManager::extend( int c )
{
	s_size += c;
	NotePlayHandle ** tmp = MM_ALLOC( NotePlayHandle*, s_size );
	MM_FREE( s_available );
	s_available = tmp;

	NotePlayHandle * n = MM_ALLOC( NotePlayHandle, c );

	for( int i=0; i < c; ++i )
	{
		s_available[++s_availableIndex] = n;
		++n;
	}
}

void NotePlayHandleManager::free()
{
	MM_FREE(s_available);
}


code:
/*
 * Oscillator.cpp - implementation of powerful oscillator-class
 *
 * 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 "Oscillator.h"

#include "BufferManager.h"
#include "Engine.h"
#include "Mixer.h"
#include "AutomatableModel.h"

Oscillator::Oscillator( const IntModel * _wave_shape_model,
				const IntModel * _mod_algo_model,
				const float & _freq,
				const float & _detuning,
				const float & _phase_offset,
				const float & _volume,
			Oscillator * _sub_osc ) :
	m_waveShapeModel( _wave_shape_model ),
	m_modulationAlgoModel( _mod_algo_model ),
	m_freq( _freq ),
	m_detuning( _detuning ),
	m_volume( _volume ),
	m_ext_phaseOffset( _phase_offset ),
	m_subOsc( _sub_osc ),
	m_phaseOffset( _phase_offset ),
	m_phase( _phase_offset ),
	m_userWave( NULL )
{
}

void Oscillator::update( sampleFrame * _ab, const fpp_t _frames,
							const ch_cnt_t _chnl )
{
	if( m_freq >= Engine::mixer()->processingSampleRate() / 2 )
	{
		BufferManager::clear( _ab, _frames );
		return;
	}
	if( m_subOsc != NULL )
	{
		switch( m_modulationAlgoModel->value() )
		{
			case PhaseModulation:
				updatePM( _ab, _frames, _chnl );
				break;
			case AmplitudeModulation:
				updateAM( _ab, _frames, _chnl );
				break;
			case SignalMix:
				updateMix( _ab, _frames, _chnl );
				break;
			case SynchronizedBySubOsc:
				updateSync( _ab, _frames, _chnl );
				break;
			case FrequencyModulation:
				updateFM( _ab, _frames, _chnl );
		}
	}
	else
	{
		updateNoSub( _ab, _frames, _chnl );
	}
}

void Oscillator::updateNoSub( sampleFrame * _ab, const fpp_t _frames,
							const ch_cnt_t _chnl )
{
	switch( m_waveShapeModel->value() )
	{
		case SineWave:
		default:
			updateNoSub<SineWave>( _ab, _frames, _chnl );
			break;
		case TriangleWave:
			updateNoSub<TriangleWave>( _ab, _frames, _chnl );
			break;
		case SawWave:
			updateNoSub<SawWave>( _ab, _frames, _chnl );
			break;
		case SquareWave:
			updateNoSub<SquareWave>( _ab, _frames, _chnl );
			break;
		case MoogSawWave:
			updateNoSub<MoogSawWave>( _ab, _frames, _chnl );
			break;
		case ExponentialWave:
			updateNoSub<ExponentialWave>( _ab, _frames, _chnl );
			break;
		case WhiteNoise:
			updateNoSub<WhiteNoise>( _ab, _frames, _chnl );
			break;
		case UserDefinedWave:
			updateNoSub<UserDefinedWave>( _ab, _frames, _chnl );
			break;
	}
}

void Oscillator::updatePM( sampleFrame * _ab, const fpp_t _frames,
							const ch_cnt_t _chnl )
{
	switch( m_waveShapeModel->value() )
	{
		case SineWave:
		default:
			updatePM<SineWave>( _ab, _frames, _chnl );
			break;
		case TriangleWave:
			updatePM<TriangleWave>( _ab, _frames, _chnl );
			break;
		case SawWave:
			updatePM<SawWave>( _ab, _frames, _chnl );
			break;
		case SquareWave:
			updatePM<SquareWave>( _ab, _frames, _chnl );
			break;
		case MoogSawWave:
			updatePM<MoogSawWave>( _ab, _frames, _chnl );
			break;
		case ExponentialWave:
			updatePM<ExponentialWave>( _ab, _frames, _chnl );
			break;
		case WhiteNoise:
			updatePM<WhiteNoise>( _ab, _frames, _chnl );
			break;
		case UserDefinedWave:
			updatePM<UserDefinedWave>( _ab, _frames, _chnl );
			break;
	}
}

void Oscillator::updateAM( sampleFrame * _ab, const fpp_t _frames,
							const ch_cnt_t _chnl )
{
	switch( m_waveShapeModel->value() )
	{
		case SineWave:
		default:
			updateAM<SineWave>( _ab, _frames, _chnl );
			break;
		case TriangleWave:
			updateAM<TriangleWave>( _ab, _frames, _chnl );
			break;
		case SawWave:
			updateAM<SawWave>( _ab, _frames, _chnl );
			break;
		case SquareWave:
			updateAM<SquareWave>( _ab, _frames, _chnl );
			break;
		case MoogSawWave:
			updateAM<MoogSawWave>( _ab, _frames, _chnl );
			break;
		case ExponentialWave:
			updateAM<ExponentialWave>( _ab, _frames, _chnl );
			break;
		case WhiteNoise:
			updateAM<WhiteNoise>( _ab, _frames, _chnl );
			break;
		case UserDefinedWave:
			updateAM<UserDefinedWave>( _ab, _frames, _chnl );
			break;
	}
}

void Oscillator::updateMix( sampleFrame * _ab, const fpp_t _frames,
							const ch_cnt_t _chnl )
{
	switch( m_waveShapeModel->value() )
	{
		case SineWave:
		default:
			updateMix<SineWave>( _ab, _frames, _chnl );
			break;
		case TriangleWave:
			updateMix<TriangleWave>( _ab, _frames, _chnl );
			break;
		case SawWave:
			updateMix<SawWave>( _ab, _frames, _chnl );
			break;
		case SquareWave:
			updateMix<SquareWave>( _ab, _frames, _chnl );
			break;
		case MoogSawWave:
			updateMix<MoogSawWave>( _ab, _frames, _chnl );
			break;
		case ExponentialWave:
			updateMix<ExponentialWave>( _ab, _frames, _chnl );
			break;
		case WhiteNoise:
			updateMix<WhiteNoise>( _ab, _frames, _chnl );
			break;
		case UserDefinedWave:
			updateMix<UserDefinedWave>( _ab, _frames, _chnl );
			break;
	}
}

void Oscillator::updateSync( sampleFrame * _ab, const fpp_t _frames,
							const ch_cnt_t _chnl )
{
	switch( m_waveShapeModel->value() )
	{
		case SineWave:
		default:
			updateSync<SineWave>( _ab, _frames, _chnl );
			break;
		case TriangleWave:
			updateSync<TriangleWave>( _ab, _frames, _chnl );
			break;
		case SawWave:
			updateSync<SawWave>( _ab, _frames, _chnl );
			break;
		case SquareWave:
			updateSync<SquareWave>( _ab, _frames, _chnl );
			break;
		case MoogSawWave:
			updateSync<MoogSawWave>( _ab, _frames, _chnl );
			break;
		case ExponentialWave:
			updateSync<ExponentialWave>( _ab, _frames, _chnl );
			break;
		case WhiteNoise:
			updateSync<WhiteNoise>( _ab, _frames, _chnl );
			break;
		case UserDefinedWave:
			updateSync<UserDefinedWave>( _ab, _frames, _chnl );
			break;
	}
}

void Oscillator::updateFM( sampleFrame * _ab, const fpp_t _frames,
							const ch_cnt_t _chnl )
{
	switch( m_waveShapeModel->value() )
	{
		case SineWave:
		default:
			updateFM<SineWave>( _ab, _frames, _chnl );
			break;
		case TriangleWave:
			updateFM<TriangleWave>( _ab, _frames, _chnl );
			break;
		case SawWave:
			updateFM<SawWave>( _ab, _frames, _chnl );
			break;
		case SquareWave:
			updateFM<SquareWave>( _ab, _frames, _chnl );
			break;
		case MoogSawWave:
			updateFM<MoogSawWave>( _ab, _frames, _chnl );
			break;
		case ExponentialWave:
			updateFM<ExponentialWave>( _ab, _frames, _chnl );
			break;
		case WhiteNoise:
			updateFM<WhiteNoise>( _ab, _frames, _chnl );
			break;
		case UserDefinedWave:
			updateFM<UserDefinedWave>( _ab, _frames, _chnl );
			break;
	}
}

// should be called every time phase-offset is changed...
inline void Oscillator::recalcPhase()
{
	if( !typeInfo<float>::isEqual( m_phaseOffset, m_ext_phaseOffset ) )
	{
		m_phase -= m_phaseOffset;
		m_phaseOffset = m_ext_phaseOffset;
		m_phase += m_phaseOffset;
	}
	m_phase = absFraction( m_phase );
}

inline bool Oscillator::syncOk( float _osc_coeff )
{
	const float v1 = m_phase;
	m_phase += _osc_coeff;
	// check whether m_phase is in next period
	return( floorf( m_phase ) > floorf( v1 ) );
}

float Oscillator::syncInit( sampleFrame * _ab, const fpp_t _frames,
						const ch_cnt_t _chnl )
{
	if( m_subOsc != NULL )
	{
		m_subOsc->update( _ab, _frames, _chnl );
	}
	recalcPhase();
	return( m_freq * m_detuning );
}

// if we have no sub-osc, we can't do any modulation... just get our samples
template<Oscillator::WaveShapes W>
void Oscillator::updateNoSub( sampleFrame * _ab, const fpp_t _frames,
							const ch_cnt_t _chnl )
{
	recalcPhase();
	const float osc_coeff = m_freq * m_detuning;

	for( fpp_t frame = 0; frame < _frames; ++frame )
	{
		_ab[frame][_chnl] = getSample<W>( m_phase ) * m_volume;
		m_phase += osc_coeff;
	}
}

// do pm by using sub-osc as modulator
template<Oscillator::WaveShapes W>
void Oscillator::updatePM( sampleFrame * _ab, const fpp_t _frames,
							const ch_cnt_t _chnl )
{
	m_subOsc->update( _ab, _frames, _chnl );
	recalcPhase();
	const float osc_coeff = m_freq * m_detuning;

	for( fpp_t frame = 0; frame < _frames; ++frame )
	{
		_ab[frame][_chnl] = getSample<W>( m_phase +
					_ab[frame][_chnl] )
							* m_volume;
		m_phase += osc_coeff;
	}
}

// do am by using sub-osc as modulator
template<Oscillator::WaveShapes W>
void Oscillator::updateAM( sampleFrame * _ab, const fpp_t _frames,
							const ch_cnt_t _chnl )
{
	m_subOsc->update( _ab, _frames, _chnl );
	recalcPhase();
	const float osc_coeff = m_freq * m_detuning;

	for( fpp_t frame = 0; frame < _frames; ++frame )
	{
		_ab[frame][_chnl] *= getSample<W>( m_phase ) * m_volume;
		m_phase += osc_coeff;
	}
}

// do mix by using sub-osc as mix-sample
template<Oscillator::WaveShapes W>
void Oscillator::updateMix( sampleFrame * _ab, const fpp_t _frames,
							const ch_cnt_t _chnl )
{
	m_subOsc->update( _ab, _frames, _chnl );
	recalcPhase();
	const float osc_coeff = m_freq * m_detuning;

	for( fpp_t frame = 0; frame < _frames; ++frame )
	{
		_ab[frame][_chnl] += getSample<W>( m_phase ) * m_volume;
		m_phase += osc_coeff;
	}
}

// sync with sub-osc (every time sub-osc starts new period, we also start new
// period)
template<Oscillator::WaveShapes W>
void Oscillator::updateSync( sampleFrame * _ab, const fpp_t _frames,
							const ch_cnt_t _chnl )
{
	const float sub_osc_coeff = m_subOsc->syncInit( _ab, _frames, _chnl );
	recalcPhase();
	const float osc_coeff = m_freq * m_detuning;

	for( fpp_t frame = 0; frame < _frames ; ++frame )
	{
		if( m_subOsc->syncOk( sub_osc_coeff ) )
		{
			m_phase = m_phaseOffset;
		}
		_ab[frame][_chnl] = getSample<W>( m_phase ) * m_volume;
		m_phase += osc_coeff;
	}
}

// do fm by using sub-osc as modulator
template<Oscillator::WaveShapes W>
void Oscillator::updateFM( sampleFrame * _ab, const fpp_t _frames,
							const ch_cnt_t _chnl )
{
	m_subOsc->update( _ab, _frames, _chnl );
	recalcPhase();
	const float osc_coeff = m_freq * m_detuning;
	const float sampleRateCorrection = 44100.0f /
				Engine::mixer()->processingSampleRate();

	for( fpp_t frame = 0; frame < _frames; ++frame )
	{
		m_phase += _ab[frame][_chnl] * sampleRateCorrection;
		_ab[frame][_chnl] = getSample<W>( m_phase ) * m_volume;
		m_phase += osc_coeff;
	}
}

template<>
inline sample_t Oscillator::getSample<Oscillator::SineWave>(
							const float _sample )
{
	return( sinSample( _sample ) );
}

template<>
inline sample_t Oscillator::getSample<Oscillator::TriangleWave>(
							const float _sample )
{
	return( triangleSample( _sample ) );
}

template<>
inline sample_t Oscillator::getSample<Oscillator::SawWave>(
							const float _sample )
{
	return( sawSample( _sample ) );
}

template<>
inline sample_t Oscillator::getSample<Oscillator::SquareWave>(
							const float _sample )
{
	return( squareSample( _sample ) );
}

template<>
inline sample_t Oscillator::getSample<Oscillator::MoogSawWave>(
							const float _sample )
{
	return( moogSawSample( _sample ) );
}

template<>
inline sample_t Oscillator::getSample<Oscillator::ExponentialWave>(
							const float _sample )
{
	return( expSample( _sample ) );
}

template<>
inline sample_t Oscillator::getSample<Oscillator::WhiteNoise>(
							const float _sample )
{
	return( noiseSample( _sample ) );
}

template<>
inline sample_t Oscillator::getSample<Oscillator::UserDefinedWave>(
							const float _sample )
{
	return( userWaveSample( _sample ) );
}

repo_name: CYBERDEViL/LMMS

license: mit


code:
#include "PathUtil.h"

#include <QDebug>
#include <QDir>
#include <QFileInfo>

#include "ConfigManager.h"

namespace PathUtil
{
	Base relativeBases[] = { Base::ProjectDir, Base::FactorySample, Base::UserSample, Base::UserVST, Base::Preset,
		Base::UserLADSPA, Base::DefaultLADSPA, Base::UserSoundfont, Base::DefaultSoundfont, Base::UserGIG, Base::DefaultGIG };

	QString baseLocation(const Base base)
	{
		QString loc = "";
		switch (base)
		{
			case Base::ProjectDir       : loc = ConfigManager::inst()->userProjectsDir(); break;
			case Base::FactorySample    :
			{
				QDir fsd = QDir(ConfigManager::inst()->factorySamplesDir());
				loc = fsd.absolutePath(); break;
			}
			case Base::UserSample       : loc = ConfigManager::inst()->userSamplesDir(); break;
			case Base::UserVST          : loc = ConfigManager::inst()->userVstDir(); break;
			case Base::Preset           : loc = ConfigManager::inst()->userPresetsDir(); break;
			case Base::UserLADSPA       : loc = ConfigManager::inst()->ladspaDir(); break;
			case Base::DefaultLADSPA    : loc = ConfigManager::inst()->userLadspaDir(); break;
			case Base::UserSoundfont    : loc = ConfigManager::inst()->sf2Dir(); break;
			case Base::DefaultSoundfont : loc = ConfigManager::inst()->userSf2Dir(); break;
			case Base::UserGIG          : loc = ConfigManager::inst()->gigDir(); break;
			case Base::DefaultGIG       : loc = ConfigManager::inst()->userGigDir(); break;
			default                   : return QString("");
		}
		return QDir::cleanPath(loc) + "/";
	}

	QDir baseQDir (const Base base)
	{
		if (base == Base::Absolute) { return QDir::root(); }
		return QDir(baseLocation(base));
	}

	QString basePrefix(const Base base)
	{
		switch (base)
		{
			case Base::ProjectDir       : return QStringLiteral("userprojects:");
			case Base::FactorySample    : return QStringLiteral("factorysample:");
			case Base::UserSample       : return QStringLiteral("usersample:");
			case Base::UserVST          : return QStringLiteral("uservst:");
			case Base::Preset           : return QStringLiteral("preset:");
			case Base::UserLADSPA       : return QStringLiteral("userladspa:");
			case Base::DefaultLADSPA    : return QStringLiteral("defaultladspa:");
			case Base::UserSoundfont    : return QStringLiteral("usersoundfont:");
			case Base::DefaultSoundfont : return QStringLiteral("defaultsoundfont:");
			case Base::UserGIG          : return QStringLiteral("usergig:");
			case Base::DefaultGIG       : return QStringLiteral("defaultgig:");
			default                   : return QStringLiteral("");
		}
	}

	Base baseLookup(const QString & path)
	{
		for (auto base: relativeBases)
		{
			QString prefix = basePrefix(base);
			if ( path.startsWith(prefix) ) { return base; }
		}
		return Base::Absolute;
	}

	QString stripPrefix(const QString & path)
	{
		return path.mid( basePrefix(baseLookup(path)).length() );
	}

	QString cleanName(const QString & path)
	{
		return stripPrefix(QFileInfo(path).baseName());
	}

	QString oldRelativeUpgrade(const QString & input)
	{
		if (input.isEmpty()) { return input; }

		//Start by assuming that the file is a user sample
		Base assumedBase = Base::UserSample;

		//Check if it's a factory sample
		QString factoryPath = baseLocation(Base::FactorySample) + input;
		QFileInfo factoryInfo(factoryPath);
		if (factoryInfo.exists()) { assumedBase = Base::FactorySample; }

		//Check if it's a VST
		QString vstPath = baseLocation(Base::UserVST) + input;
		QFileInfo vstInfo(vstPath);
		if (vstInfo.exists()) { assumedBase = Base::UserVST; }

		//Assume we've found the correct base location, return the full path
		return basePrefix(assumedBase) + input;
	}

	QString toAbsolute(const QString & input)
	{
		//First, do no harm to absolute paths
		QFileInfo inputFileInfo = QFileInfo(input);
		if (inputFileInfo.isAbsolute()) { return input; }
		//Next, handle old relative paths with no prefix
		QString upgraded = input.contains(":") ? input : oldRelativeUpgrade(input);

		Base base = baseLookup(upgraded);
		return baseLocation(base) + upgraded.remove(0, basePrefix(base).length());
	}

	QString relativeOrAbsolute(const QString & input, const Base base)
	{
		if (input.isEmpty()) { return input; }
		QString absolutePath = toAbsolute(input);
		if (base == Base::Absolute) { return absolutePath; }
		QString relativePath = baseQDir(base).relativeFilePath(absolutePath);
		return relativePath.startsWith("..") ? absolutePath : relativePath;
	}

	QString toShortestRelative(const QString & input)
	{
		QFileInfo inputFileInfo = QFileInfo(input);
		QString absolutePath = inputFileInfo.isAbsolute() ? input : toAbsolute(input);

		Base shortestBase = Base::Absolute;
		QString shortestPath = relativeOrAbsolute(absolutePath, shortestBase);
		for (auto base: relativeBases)
		{
			QString otherPath = relativeOrAbsolute(absolutePath, base);
			if (otherPath.length() < shortestPath.length())
			{
				shortestBase = base;
				shortestPath = otherPath;
			}
		}
		return basePrefix(shortestBase) + relativeOrAbsolute(absolutePath, shortestBase);
	}
}


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

#include "PeakController.h"

#include <cmath>

#include <QDomElement>
#include <QMessageBox>

#include "Mixer.h"
#include "EffectChain.h"
#include "plugins/peak_controller_effect/peak_controller_effect.h"

PeakControllerEffectVector PeakController::s_effects;
int PeakController::m_getCount;
int PeakController::m_loadCount;
bool PeakController::m_buggedFile;

PeakController::PeakController( Model * _parent,
		PeakControllerEffect * _peak_effect ) :
	Controller( Controller::PeakController, _parent, tr( "Peak Controller" ) ),
	m_peakEffect( _peak_effect ),
	m_currentSample( 0.0f )
{
	setSampleExact( true );
	if( m_peakEffect )
	{
		connect( m_peakEffect, SIGNAL( destroyed( ) ),
			this, SLOT( handleDestroyedEffect( ) ) );
	}
	connect( Engine::mixer(), SIGNAL( sampleRateChanged() ), this, SLOT( updateCoeffs() ) );
	connect( m_peakEffect->attackModel(), SIGNAL( dataChanged() ),
			this, SLOT( updateCoeffs() ), Qt::DirectConnection );
	connect( m_peakEffect->decayModel(), SIGNAL( dataChanged() ),
			this, SLOT( updateCoeffs() ), Qt::DirectConnection );
	m_coeffNeedsUpdate = true;
}

PeakController::~PeakController()
{
	if( m_peakEffect != NULL && m_peakEffect->effectChain() != NULL )
	{
		m_peakEffect->effectChain()->removeEffect( m_peakEffect );
	}
}

void PeakController::updateValueBuffer()
{
	if( m_coeffNeedsUpdate )
	{
		const float ratio = 44100.0f / Engine::mixer()->processingSampleRate();
		m_attackCoeff = 1.0f - powf( 2.0f, -0.3f * ( 1.0f - m_peakEffect->attackModel()->value() ) * ratio );
		m_decayCoeff = 1.0f -  powf( 2.0f, -0.3f * ( 1.0f - m_peakEffect->decayModel()->value()  ) * ratio );
		m_coeffNeedsUpdate = false;
	}

	if( m_peakEffect )
	{
		float targetSample = m_peakEffect->lastSample();
		if( m_currentSample != targetSample )
		{
			const f_cnt_t frames = Engine::mixer()->framesPerPeriod();
			float * values = m_valueBuffer.values();

			for( f_cnt_t f = 0; f < frames; ++f )
			{
				const float diff = ( targetSample - m_currentSample );
				if( m_currentSample < targetSample ) // going up...
				{
					m_currentSample += diff * m_attackCoeff;
				}
				else if( m_currentSample > targetSample ) // going down
				{
					m_currentSample += diff * m_decayCoeff;
				}
				values[f] = m_currentSample;
			}
		}
		else
		{
			m_valueBuffer.fill( m_currentSample );
		}
	}
	else
	{
		m_valueBuffer.fill( 0 );
	}
	m_bufferLastUpdated = s_periods;
}

void PeakController::updateCoeffs()
{
	m_coeffNeedsUpdate = true;
}

void PeakController::handleDestroyedEffect( )
{
	// possible race condition...
	//printf("disconnecting effect\n");
	disconnect( m_peakEffect );
	m_peakEffect = NULL;
	//deleteLater();
	delete this;
}

void PeakController::saveSettings( QDomDocument & _doc, QDomElement & _this )
{
	if( m_peakEffect )
	{
		Controller::saveSettings( _doc, _this );

		_this.setAttribute( "effectId", m_peakEffect->m_effectId );
	}
}

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

	int effectId = _this.attribute( "effectId" ).toInt();
	if( m_buggedFile == true )
	{
		effectId = m_loadCount++;
	}

	PeakControllerEffectVector::Iterator i;
	for( i = s_effects.begin(); i != s_effects.end(); ++i )
	{
		if( (*i)->m_effectId == effectId )
		{
			m_peakEffect = *i;
			return;
		}
	}
}

//Backward compatibility function for bug in <= 0.4.15
void PeakController::initGetControllerBySetting()
{
	m_loadCount = 0;
	m_getCount = 0;
	m_buggedFile = false;
}

PeakController * PeakController::getControllerBySetting(const QDomElement & _this )
{
	int effectId = _this.attribute( "effectId" ).toInt();

	PeakControllerEffectVector::Iterator i;

	//Backward compatibility for bug in <= 0.4.15 . For >= 1.0.0 ,
	//foundCount should always be 1 because m_effectId is initialized with rand()
	int foundCount = 0;
	if( m_buggedFile == false )
	{
		for( i = s_effects.begin(); i != s_effects.end(); ++i )
		{
			if( (*i)->m_effectId == effectId )
			{
				foundCount++;
			}
		}
		if( foundCount >= 2 )
		{
			m_buggedFile = true;
			int newEffectId = 0;
			for( i = s_effects.begin(); i != s_effects.end(); ++i )
			{
				(*i)->m_effectId = newEffectId++;
			}
			QMessageBox msgBox;
			msgBox.setIcon( QMessageBox::Information );
			msgBox.setWindowTitle( tr("Peak Controller Bug") );
			msgBox.setText( tr("Due to a bug in older version of LMMS, the peak "
							   "controllers may not be connect properly. "
							   "Please ensure that peak controllers are connected "
							   "properly and re-save this file. "
							   "Sorry for any inconvenience caused.") );
			msgBox.setStandardButtons(QMessageBox::Ok);
			msgBox.exec();
		}
	}

	if( m_buggedFile == true )
	{
		effectId = m_getCount;
	}
	m_getCount++; //NB: m_getCount should be increased even m_buggedFile is false

	for( i = s_effects.begin(); i != s_effects.end(); ++i )
	{
		if( (*i)->m_effectId == effectId )
		{
			return (*i)->controller();
		}
	}

	return NULL;
}

QString PeakController::nodeName() const
{
	return( "Peakcontroller" );
}

ControllerDialog * PeakController::createDialog( QWidget * _parent )
{
	return new PeakControllerDialog( this, _parent );
}


code:
/*
 * PerfLog.cpp - Small performance logger
 *
 * Copyright (c) 2017-2018 LMMS Developers
 *
 * 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 "PerfLog.h"

#include "lmmsconfig.h"

#if defined(LMMS_HAVE_SYS_TIMES_H) && defined(LMMS_HAVE_UNISTD_H)
#	define USE_POSIX_TIME
#endif

#ifdef USE_POSIX_TIME
#	include <unistd.h>
#	include <sys/times.h>
#endif

PerfTime::PerfTime()
	: m_real(-1)
{
}

clock_t PerfTime::real() const
{
	return m_real;
}

clock_t PerfTime::user() const
{
	return m_user;
}

clock_t PerfTime::system() const
{
	return m_system;
}

bool PerfTime::valid() const
{
	return m_real != -1;
}

PerfTime PerfTime::now()
{
	PerfTime time;
#ifdef USE_POSIX_TIME
	tms t;
	time.m_real = times(&t);
	time.m_user = t.tms_utime;
	time.m_system = t.tms_stime;
	if (time.m_real == -1) { qWarning("PerfTime: now failed"); }
#endif
	return time;
}

clock_t PerfTime::ticksPerSecond()
{
	static long clktck = 0;
#ifdef USE_POSIX_TIME
	if (!clktck) {
		if ((clktck = sysconf(_SC_CLK_TCK)) < 0) {
			qWarning("PerfLog::end sysconf()");
		}
	}
#endif
	return clktck;
}

PerfTime operator-(const PerfTime& lhs, const PerfTime& rhs)
{
	PerfTime diff;
	diff.m_real = lhs.m_real - rhs.m_real;
	diff.m_user = lhs.m_user - rhs.m_user;
	diff.m_system = lhs.m_system - rhs.m_system;
	return diff;
}

PerfLogTimer::PerfLogTimer(const QString& what)
	: name(what)
{
	begin();
}

PerfLogTimer::~PerfLogTimer()
{
	end();
}

void PerfLogTimer::begin()
{
	begin_time = PerfTime::now();
}

void PerfLogTimer::end()
{
	if (! begin_time.valid()) {
		return;
	}

	long clktck = PerfTime::ticksPerSecond();

	PerfTime d = PerfTime::now() - begin_time;
	qWarning("PERFLOG | %20s | %.2fuser, %.2fsystem %.2felapsed",
			 qPrintable(name),
			 d.user() / (double)clktck,
			 d.system() / (double)clktck,
			 d.real() / (double)clktck);

	// Invalidate so destructor won't call print another log entry
	begin_time = PerfTime();
}


code:
/*
 * Piano.cpp - implementation of piano-widget used in instrument-track-window
 *             for testing + according model 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 Piano.cpp
 *  \brief A piano keyboard to play notes on in the instrument plugin window.
 */

/*
 * \mainpage Instrument plugin keyboard display classes
 *
 * \section introduction Introduction
 *
 * \todo fill this out
 * \todo write isWhite inline function and replace throughout
 */

#include "Piano.h"

#include "InstrumentTrack.h"

/*! The black / white order of keys as they appear on the keyboard.
 */
static const Piano::KeyTypes KEY_ORDER[] =
{
//	C                CIS              D                DIS
	Piano::WhiteKey, Piano::BlackKey, Piano::WhiteKey, Piano::BlackKey,
//	E                F                FIS              G
	Piano::WhiteKey, Piano::WhiteKey, Piano::BlackKey, Piano::WhiteKey,
//	GIS              A                AIS              B
	Piano::BlackKey, Piano::WhiteKey, Piano::BlackKey, Piano::WhiteKey
} ;

/*! \brief Create a new keyboard display
 *
 *  \param _it the InstrumentTrack window to attach to
 */
Piano::Piano( InstrumentTrack* track ) :
	Model( NULL ),              /*!< base class ctor */
	m_instrumentTrack( track ),
	m_midiEvProc( track )        /*!< the InstrumentTrack Model */
{
	for( int i = 0; i < NumKeys; ++i )
	{
		m_pressedKeys[i] = false;
	}

}

/*! \brief Turn a key on or off
 *
 *  \param key the key number to change
 *  \param state the state to set the key to
 */
void Piano::setKeyState( int key, bool state )
{
	if( isValidKey( key ) )
	{
		m_pressedKeys[key] = state;

		emit dataChanged();
	}
}

/*! \brief Handle a note being pressed on our keyboard display
 *
 *  \param key the key being pressed
 */
void Piano::handleKeyPress( int key, int midiVelocity )
{
	if( midiVelocity == -1 )
	{
		midiVelocity = m_instrumentTrack->midiPort()->baseVelocity();
	}
	if( isValidKey( key ) )
	{
		m_midiEvProc->processInEvent( MidiEvent( MidiNoteOn, -1, key, midiVelocity ) );
		m_pressedKeys[key] = true;
	}
}

/*! \brief Handle a note being released on our keyboard display
 *
 *  \param key the key being releassed
 */
void Piano::handleKeyRelease( int key )
{
	if( isValidKey( key ) )
	{
		m_midiEvProc->processInEvent( MidiEvent( MidiNoteOff, -1, key, 0 ) );
		m_pressedKeys[key] = false;
	}
}

bool Piano::isBlackKey( int key )
{
	int keyCode = key % KeysPerOctave;

	return KEY_ORDER[keyCode] == Piano::BlackKey;
}

bool Piano::isWhiteKey( int key )
{
	return !isBlackKey( key );
}


code:
/*
 * PlayHandle.cpp - base class PlayHandle - core of rendering engine
 *
 * 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 "PlayHandle.h"
#include "BufferManager.h"
#include "Engine.h"
#include "Mixer.h"

#include <QtCore/QThread>
#include <QDebug>

#include <iterator>

PlayHandle::PlayHandle(const Type type, f_cnt_t offset) :
		m_type(type),
		m_offset(offset),
		m_affinity(QThread::currentThread()),
		m_playHandleBuffer(BufferManager::acquire()),
		m_bufferReleased(true),
		m_usesBuffer(true)
{
}

PlayHandle::~PlayHandle()
{
	BufferManager::release(m_playHandleBuffer);
}

void PlayHandle::doProcessing()
{
	if( m_usesBuffer )
	{
		m_bufferReleased = false;
		BufferManager::clear(m_playHandleBuffer, Engine::mixer()->framesPerPeriod());
		play( buffer() );
	}
	else
	{
		play( NULL );
	}
}

void PlayHandle::releaseBuffer()
{
	m_bufferReleased = true;
}

sampleFrame* PlayHandle::buffer()
{
	return m_bufferReleased ? nullptr : reinterpret_cast<sampleFrame*>(m_playHandleBuffer);
};


code:
/*
 * Plugin.cpp - implementation of plugin-class including plugin-loader
 *
 * 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 "Plugin.h"

#include <QtGlobal>
#include <QtCore/QDebug>
#include <QtCore/QDir>
#include <QtCore/QLibrary>
#include <QMessageBox>

#include "embed.h"
#include "Engine.h"
#include "GuiApplication.h"
#include "DummyPlugin.h"
#include "AutomatableModel.h"
#include "Song.h"

static PixmapLoader dummyLoader;

static Plugin::Descriptor dummyPluginDescriptor =
{
	"dummy",
	"dummy",
	QT_TRANSLATE_NOOP( "PluginBrowser", "no description" ),
	"Tobias Doerffel <tobydox/at/users.sf.net>",
	0x0100,
	Plugin::Undefined,
	&dummyLoader,
	NULL
} ;

Plugin::Plugin(const Descriptor * descriptor, Model * parent, const
		Descriptor::SubPluginFeatures::Key* key) :
	Model(parent),
	JournallingObject(),
	m_descriptor(descriptor),
	m_key(key ? *key : Descriptor::SubPluginFeatures::Key(m_descriptor))
{
	if( m_descriptor == NULL )
	{
		m_descriptor = &dummyPluginDescriptor;
	}
}

Plugin::~Plugin()
{
}

template<class T>
T use_this_or(T this_param, T or_param)
{
	return this_param ? this_param : or_param;
}

QString use_this_or(QString this_param, QString or_param)
{
	return this_param.isNull() ? or_param : this_param;
}

QString Plugin::displayName() const
{
	return Model::displayName().isEmpty() // currently always empty
		? (m_descriptor->subPluginFeatures && m_key.isValid())
			// get from sub plugin
			? m_key.displayName()
			// get from plugin
			: m_descriptor->displayName
		: Model::displayName();
}

const PixmapLoader* Plugin::logo() const
{
	return (m_descriptor->subPluginFeatures && m_key.isValid())
		? m_key.logo()
		: m_descriptor->logo;
}

QString Plugin::Descriptor::SubPluginFeatures::Key::additionalFileExtensions() const
{
	Q_ASSERT(isValid());
	return desc->subPluginFeatures
		// get from sub plugin
		? desc->subPluginFeatures->additionalFileExtensions(*this)
		// no sub plugin, so no *additional* file extensions
		: QString();
}

QString Plugin::Descriptor::SubPluginFeatures::Key::displayName() const
{
	Q_ASSERT(isValid());
	return desc->subPluginFeatures
		// get from sub plugin
		? use_this_or(desc->subPluginFeatures->displayName(*this),
			QString::fromUtf8(desc->displayName))
		// get from plugin
		: desc->displayName;
}

const PixmapLoader* Plugin::Descriptor::SubPluginFeatures::Key::logo() const
{
	Q_ASSERT(isValid());
	return desc->subPluginFeatures
		? use_this_or(desc->subPluginFeatures->logo(*this), desc->logo)
		: desc->logo;
}

QString Plugin::Descriptor::SubPluginFeatures::Key::description() const
{
	Q_ASSERT(isValid());
	return desc->subPluginFeatures
		? use_this_or(desc->subPluginFeatures->description(*this),
			QString::fromUtf8(desc->description))
		: desc->description;
}

void Plugin::loadFile( const QString & )
{
}

AutomatableModel * Plugin::childModel( const QString & )
{
	static FloatModel fm;
	return &fm;
}

#include "PluginFactory.h"
Plugin * Plugin::instantiateWithKey(const QString& pluginName, Model * parent,
				const Descriptor::SubPluginFeatures::Key *key,
				bool keyFromDnd)
{
	if(keyFromDnd)
		Q_ASSERT(!key);
	const Descriptor::SubPluginFeatures::Key *keyPtr = keyFromDnd
		? static_cast<Plugin::Descriptor::SubPluginFeatures::Key*>(Engine::pickDndPluginKey())
		: key;
	const PluginFactory::PluginInfo& pi = pluginFactory->pluginInfo(pluginName.toUtf8());
	if(keyPtr)
	{
		// descriptor is not yet set when loading - set it now
		Descriptor::SubPluginFeatures::Key keyCopy = *keyPtr;
		keyCopy.desc = pi.descriptor;
		return Plugin::instantiate(pluginName, parent, &keyCopy);
	}
	else
		return Plugin::instantiate(pluginName, parent,
			// the keys are never touched anywhere
			const_cast<Descriptor::SubPluginFeatures::Key *>(keyPtr));
}

Plugin * Plugin::instantiate(const QString& pluginName, Model * parent,
								void *data)
{
	const PluginFactory::PluginInfo& pi = pluginFactory->pluginInfo(pluginName.toUtf8());

	Plugin* inst;
	if( pi.isNull() )
	{
		if( gui )
		{
			QMessageBox::information( NULL,
				tr( "Plugin not found" ),
				tr( "The plugin \"%1\" wasn't found or could not be loaded!\nReason: \"%2\"" ).
						arg( pluginName ).arg( pluginFactory->errorString(pluginName) ),
				QMessageBox::Ok | QMessageBox::Default );
		}
		inst = new DummyPlugin();
	}
	else
	{
		InstantiationHook instantiationHook;
		if ((instantiationHook = ( InstantiationHook ) pi.library->resolve( "lmms_plugin_main" )))
		{
			inst = instantiationHook(parent, data);
			if(!inst) {
				inst = new DummyPlugin();
			}
		}
		else
		{
			if( gui )
			{
				QMessageBox::information( NULL,
					tr( "Error while loading plugin" ),
					tr( "Failed to load plugin \"%1\"!").arg( pluginName ),
					QMessageBox::Ok | QMessageBox::Default );
			}
			inst = new DummyPlugin();
		}
	}

	return inst;
}

void Plugin::collectErrorForUI( QString errMsg )
{
	Engine::getSong()->collectError( errMsg );
}

PluginView * Plugin::createView( QWidget * parent )
{
	PluginView * pv = instantiateView( parent );
	if( pv != NULL )
	{
		pv->setModel( this );
	}
	return pv;
}

Plugin::Descriptor::SubPluginFeatures::Key::Key( const QDomElement & key ) :
	desc( NULL ),
	name( key.attribute( "key" ) ),
	attributes()
{
	QDomNodeList l = key.elementsByTagName( "attribute" );
	for( int i = 0; !l.item( i ).isNull(); ++i )
	{
		QDomElement e = l.item( i ).toElement();
		attributes[e.attribute( "name" )] = e.attribute( "value" );
	}

}

QDomElement Plugin::Descriptor::SubPluginFeatures::Key::saveXML(
						QDomDocument & doc ) const
{
	QDomElement e = doc.createElement( "key" );
	for( AttributeMap::ConstIterator it = attributes.begin();
		it != attributes.end(); ++it )
	{
		QDomElement a = doc.createElement( "attribute" );
		a.setAttribute( "name", it.key() );
		a.setAttribute( "value", it.value() );
		e.appendChild( a );
	}
	return e;
}


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

#include "PluginFactory.h"

#include <QtCore/QCoreApplication>
#include <QtCore/QDebug>
#include <QtCore/QDir>
#include <QtCore/QLibrary>
#include "lmmsconfig.h"

#include "ConfigManager.h"
#include "Plugin.h"
#include "embed.h"

#ifdef LMMS_BUILD_WIN32
	QStringList nameFilters("*.dll");
#else
	QStringList nameFilters("lib*.so");
#endif

qint64 qHash(const QFileInfo& fi)
{
	return qHash(fi.absoluteFilePath());
}

std::unique_ptr<PluginFactory> PluginFactory::s_instance;

PluginFactory::PluginFactory()
{
	setupSearchPaths();
	discoverPlugins();
}

PluginFactory::~PluginFactory()
{
}

void PluginFactory::setupSearchPaths()
{
	// Adds a search path relative to the main executable if the path exists.
	auto addRelativeIfExists = [](const QString & path) {
		QDir dir(qApp->applicationDirPath());
		if (!path.isEmpty() && dir.cd(path)) {
			QDir::addSearchPath("plugins", dir.absolutePath());
		}
	};

	// We're either running LMMS installed on an Unixoid or we're running a
	// portable version like we do on Windows.
	// We want to find our plugins in both cases:
	//  (a) Installed (Unix):
	//      e.g. binary at /usr/bin/lmms - plugin dir at /usr/lib/lmms/
	//  (b) Portable:
	//      e.g. binary at "C:/Program Files/LMMS/lmms.exe"
	//           plugins at "C:/Program Files/LMMS/plugins/"

#ifndef LMMS_BUILD_WIN32
	addRelativeIfExists("../lib/lmms"); // Installed
#endif
	addRelativeIfExists("plugins"); // Portable
#ifdef PLUGIN_DIR // We may also have received a relative directory via a define
	addRelativeIfExists(PLUGIN_DIR);
#endif
	// Or via an environment variable:
	QString env_path;
	if (!(env_path = qgetenv("LMMS_PLUGIN_DIR")).isEmpty())
		QDir::addSearchPath("plugins", env_path);

	QDir::addSearchPath("plugins", ConfigManager::inst()->workingDir() + "plugins");
}

PluginFactory* PluginFactory::instance()
{
	if (s_instance == nullptr)
		s_instance.reset(new PluginFactory());

	return s_instance.get();
}

const Plugin::DescriptorList PluginFactory::descriptors() const
{
	return m_descriptors.values();
}

const Plugin::DescriptorList PluginFactory::descriptors(Plugin::PluginTypes type) const
{
	return m_descriptors.values(type);
}

const PluginFactory::PluginInfoList& PluginFactory::pluginInfos() const
{
	return m_pluginInfos;
}

const PluginFactory::PluginInfoAndKey PluginFactory::pluginSupportingExtension(const QString& ext)
{
	return m_pluginByExt.value(ext, PluginInfoAndKey());
}

const PluginFactory::PluginInfo PluginFactory::pluginInfo(const char* name) const
{
	for (const PluginInfo& info : m_pluginInfos)
	{
		if (qstrcmp(info.descriptor->name, name) == 0)
			return info;
	}
	return PluginInfo();
}

QString PluginFactory::errorString(QString pluginName) const
{
	static QString notfound = qApp->translate("PluginFactory", "Plugin not found.");
	return m_errors.value(pluginName, notfound);
}

void PluginFactory::discoverPlugins()
{
	DescriptorMap descriptors;
	PluginInfoList pluginInfos;
	m_pluginByExt.clear();

	QSet<QFileInfo> files;
	for (const QString& searchPath : QDir::searchPaths("plugins"))
	{
#if (QT_VERSION >= QT_VERSION_CHECK(5,14,0))
		auto discoveredPluginList = QDir(searchPath).entryInfoList(nameFilters);
		files.unite(QSet<QFileInfo>(discoveredPluginList.begin(), discoveredPluginList.end()));
#else
		files.unite(QDir(searchPath).entryInfoList(nameFilters).toSet());
#endif
	}

	// Cheap dependency handling: zynaddsubfx needs ZynAddSubFxCore. By loading
	// all libraries twice we ensure that libZynAddSubFxCore is found.
	for (const QFileInfo& file : files)
	{
		QLibrary(file.absoluteFilePath()).load();
	}

	for (const QFileInfo& file : files)
	{
		auto library = std::make_shared<QLibrary>(file.absoluteFilePath());
		if (! library->load()) {
			m_errors[file.baseName()] = library->errorString();
			qWarning("%s", library->errorString().toLocal8Bit().data());
			continue;
		}

		Plugin::Descriptor* pluginDescriptor = nullptr;
		if (library->resolve("lmms_plugin_main"))
		{
			QString descriptorName = file.baseName() + "_plugin_descriptor";
			if( descriptorName.left(3) == "lib" )
			{
				descriptorName = descriptorName.mid(3);
			}

			pluginDescriptor = reinterpret_cast<Plugin::Descriptor*>(library->resolve(descriptorName.toUtf8().constData()));
			if(pluginDescriptor == nullptr)
			{
				qWarning() << qApp->translate("PluginFactory", "LMMS plugin %1 does not have a plugin descriptor named %2!").
							  arg(file.absoluteFilePath()).arg(descriptorName);
				continue;
			}
		}

		if(pluginDescriptor)
		{
			PluginInfo info;
			info.file = file;
			info.library = library;
			info.descriptor = pluginDescriptor;
			pluginInfos << info;

			auto addSupportedFileTypes =
				[this](QString supportedFileTypes,
					const PluginInfo& info,
					const Plugin::Descriptor::SubPluginFeatures::Key* key = nullptr)
			{
				if(!supportedFileTypes.isNull())
				{
					for (const QString& ext : supportedFileTypes.split(','))
					{
						//qDebug() << "Plugin " << info.name()
						//	<< "supports" << ext;
						PluginInfoAndKey infoAndKey;
						infoAndKey.info = info;
						infoAndKey.key = key
							? *key
							: Plugin::Descriptor::SubPluginFeatures::Key();
						m_pluginByExt.insert(ext, infoAndKey);
					}
				}
			};

			if (info.descriptor->supportedFileTypes)
				addSupportedFileTypes(QString(info.descriptor->supportedFileTypes), info);

			if (info.descriptor->subPluginFeatures)
			{
				Plugin::Descriptor::SubPluginFeatures::KeyList
					subPluginKeys;
				info.descriptor->subPluginFeatures->listSubPluginKeys(
					info.descriptor,
					subPluginKeys);
				for(const Plugin::Descriptor::SubPluginFeatures::Key& key
					: subPluginKeys)
				{
					addSupportedFileTypes(key.additionalFileExtensions(), info, &key);
				}
			}

			descriptors.insert(info.descriptor->type, info.descriptor);
		}
	}

	m_pluginInfos = pluginInfos;
	m_descriptors = descriptors;
}

const QString PluginFactory::PluginInfo::name() const
{
	return descriptor ? descriptor->name : QString();
}


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

#include <QDebug>

#include "PluginIssue.h"

const char *PluginIssue::msgFor(const PluginIssueType &it)
{
	switch (it)
	{
		case unknownPortFlow:
			return "unknown port flow for mandatory port";
		case unknownPortType:
			return "unknown port type for mandatory port";
		case tooManyInputChannels:
			return "too many audio input channels";
		case tooManyOutputChannels:
			return "too many audio output channels";
		case tooManyMidiInputChannels:
			return "too many MIDI input channels";
		case tooManyMidiOutputChannels:
			return "too many MIDI output channels";
		case noOutputChannel:
			return "no audio output channel";
		case portHasNoDef:
			return "port is missing default value";
		case portHasNoMin:
			return "port is missing min value";
		case portHasNoMax:
			return "port is missing max value";
		case minGreaterMax:
			return "port minimum is greater than maximum";
		case defaultValueNotInRange:
			return "default value is not in range [min, max]";
		case logScaleMinMissing:
			return "logscale requires minimum value";
		case logScaleMaxMissing:
			return "logscale requires maximum value";
		case logScaleMinMaxDifferentSigns:
			return "logscale with min < 0 < max";
		case featureNotSupported:
			return "required feature not supported";
		case badPortType:
			return "unsupported port type";
		case blacklisted:
			return "blacklisted plugin";
		case noIssue:
			return nullptr;
	}
	return nullptr;
}

bool PluginIssue::operator==(const PluginIssue &other) const
{
	return (m_issueType == other.m_issueType) && (m_info == other.m_info);
}

bool PluginIssue::operator<(const PluginIssue &other) const
{
	return (m_issueType != other.m_issueType)
			? m_issueType < other.m_issueType
			: m_info < other.m_info;
}

QDebug operator<<(QDebug stream, const PluginIssue &iss)
{
	stream << PluginIssue::msgFor(iss.m_issueType);
	if (iss.m_info.length())
	{
		stream.nospace() << ": " << iss.m_info.c_str();
	}
	return stream;
}


code:
/*
 * PresetPreviewPlayHandle.cpp - implementation of class PresetPreviewPlayHandle
 *
 * 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 <QFileInfo>

#include "PresetPreviewPlayHandle.h"
#include "Engine.h"
#include "Instrument.h"
#include "InstrumentTrack.h"
#include "Mixer.h"
#include "PluginFactory.h"
#include "ProjectJournal.h"
#include "TrackContainer.h"

#include <atomic>

// invisible track-container which is needed as parent for preview-channels
class PreviewTrackContainer : public TrackContainer
{
public:
	PreviewTrackContainer() :
		m_previewInstrumentTrack( NULL ),
		m_previewNote( NULL ),
		m_dataMutex()
	{
		setJournalling( false );
		m_previewInstrumentTrack = dynamic_cast<InstrumentTrack *>( Track::create( Track::InstrumentTrack, this ) );
		m_previewInstrumentTrack->setJournalling( false );
		m_previewInstrumentTrack->setPreviewMode( true );
	}

	virtual ~PreviewTrackContainer()
	{
	}

	QString nodeName() const override
	{
		return "previewtrackcontainer";
	}

	InstrumentTrack* previewInstrumentTrack()
	{
		return m_previewInstrumentTrack;
	}

	NotePlayHandle* previewNote()
	{
		return m_previewNote.load(std::memory_order_acquire);
	}

	void setPreviewNote( NotePlayHandle * _note )
	{
		m_previewNote.store(_note, std::memory_order_release);
	}

	bool testAndSetPreviewNote( NotePlayHandle * expectedVal, NotePlayHandle * newVal )
	{
		return m_previewNote.compare_exchange_strong(expectedVal, newVal);
	}

	void lockData()
	{
		m_dataMutex.lock();
	}

	void unlockData()
	{
		m_dataMutex.unlock();
	}

	bool isPreviewing()
	{
		bool ret = !m_dataMutex.tryLock();
		if( ret == false )
		{
			m_dataMutex.unlock();
		}
		return ret;
	}

private:
	InstrumentTrack* m_previewInstrumentTrack;
	std::atomic<NotePlayHandle*> m_previewNote;
	QMutex m_dataMutex;

	friend class PresetPreviewPlayHandle;

} ;

PreviewTrackContainer * PresetPreviewPlayHandle::s_previewTC;

PresetPreviewPlayHandle::PresetPreviewPlayHandle( const QString & _preset_file, bool _load_by_plugin, DataFile *dataFile ) :
	PlayHandle( TypePresetPreviewHandle ),
	m_previewNote(nullptr)
{
	setUsesBuffer( false );

	s_previewTC->lockData();

	Engine::mixer()->requestChangeInModel();
	s_previewTC->setPreviewNote( nullptr );
	s_previewTC->previewInstrumentTrack()->silenceAllNotes();
	Engine::mixer()->doneChangeInModel();

	const bool j = Engine::projectJournal()->isJournalling();
	Engine::projectJournal()->setJournalling( false );

	if( _load_by_plugin )
	{
		Instrument * i = s_previewTC->previewInstrumentTrack()->instrument();
		const QString ext = QFileInfo( _preset_file ).
							suffix().toLower();
		if( i == NULL || !i->descriptor()->supportsFileType( ext ) )
		{
			const PluginFactory::PluginInfoAndKey& infoAndKey =
				pluginFactory->pluginSupportingExtension(ext);
			i = s_previewTC->previewInstrumentTrack()->
				loadInstrument(infoAndKey.info.name(), &infoAndKey.key);
		}
		if( i != NULL )
		{
			i->loadFile( _preset_file );
		}
	}
	else
	{
		bool dataFileCreated = false;
		if( dataFile == 0 )
		{
			dataFile = new DataFile( _preset_file );
			dataFileCreated = true;
		}

		s_previewTC->previewInstrumentTrack()->loadTrackSpecificSettings(
					dataFile->content().firstChild().toElement());

		if( dataFileCreated )
		{
			delete dataFile;
		}
	}
	dataFile = 0;
	// make sure, our preset-preview-track does not appear in any MIDI-
	// devices list, so just disable receiving/sending MIDI-events at all
	s_previewTC->previewInstrumentTrack()->
				midiPort()->setMode( MidiPort::Disabled );

	Engine::mixer()->requestChangeInModel();
	// create note-play-handle for it
	m_previewNote = NotePlayHandleManager::acquire(
			s_previewTC->previewInstrumentTrack(), 0,
			typeInfo<f_cnt_t>::max() / 2,
				Note( 0, 0, DefaultKey, 100 ) );

	setAudioPort( s_previewTC->previewInstrumentTrack()->audioPort() );

	s_previewTC->setPreviewNote( m_previewNote );

	Engine::mixer()->addPlayHandle( m_previewNote );

	Engine::mixer()->doneChangeInModel();
	s_previewTC->unlockData();
	Engine::projectJournal()->setJournalling( j );
}

PresetPreviewPlayHandle::~PresetPreviewPlayHandle()
{
	Engine::mixer()->requestChangeInModel();
	// not muted by other preset-preview-handle?
	if (s_previewTC->testAndSetPreviewNote(m_previewNote, nullptr))
	{
		m_previewNote->noteOff();
	}
	Engine::mixer()->doneChangeInModel();
}

void PresetPreviewPlayHandle::play( sampleFrame * _working_buffer )
{
	// Do nothing; the preview instrument is played by m_previewNote, which
	// has been added to the mixer
}

bool PresetPreviewPlayHandle::isFinished() const
{
	return m_previewNote->isMuted();
}

bool PresetPreviewPlayHandle::isFromTrack( const Track * _track ) const
{
	return s_previewTC && s_previewTC->previewInstrumentTrack() == _track;
}

void PresetPreviewPlayHandle::init()
{
	if( !s_previewTC )
	{
		s_previewTC = new PreviewTrackContainer;
	}
}

void PresetPreviewPlayHandle::cleanup()
{
	delete s_previewTC;
	s_previewTC = NULL;
}

ConstNotePlayHandleList PresetPreviewPlayHandle::nphsOfInstrumentTrack(
						const InstrumentTrack * _it )
{
	ConstNotePlayHandleList cnphv;
	if( s_previewTC->previewNote() != NULL &&
		s_previewTC->previewNote()->instrumentTrack() == _it )
	{
		cnphv.push_back( s_previewTC->previewNote() );
	}
	return cnphv;
}

bool PresetPreviewPlayHandle::isPreviewing()
{
	if (s_previewTC) {
		return s_previewTC->isPreviewing();
	}
	return false;
}


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

#include <cstdlib>

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

//! Avoid clashes between loaded IDs (have the bit cleared)
//! and newly created IDs (have the bit set)
static const int EO_ID_MSB = 1 << 23;

const int ProjectJournal::MAX_UNDO_STATES = 100; // TODO: make this configurable in settings

ProjectJournal::ProjectJournal() :
	m_joIDs(),
	m_undoCheckPoints(),
	m_redoCheckPoints(),
	m_journalling( false )
{
}

ProjectJournal::~ProjectJournal()
{
}

void ProjectJournal::undo()
{
	while( !m_undoCheckPoints.isEmpty() )
	{
		CheckPoint c = m_undoCheckPoints.pop();
		JournallingObject *jo = m_joIDs[c.joID];

		if( jo )
		{
			DataFile curState( DataFile::JournalData );
			jo->saveState( curState, curState.content() );
			m_redoCheckPoints.push( CheckPoint( c.joID, curState ) );

			bool prev = isJournalling();
			setJournalling( false );
			jo->restoreState( c.data.content().firstChildElement() );
			setJournalling( prev );
			Engine::getSong()->setModified();
			break;
		}
	}
}

void ProjectJournal::redo()
{
	while( !m_redoCheckPoints.isEmpty() )
	{
		CheckPoint c = m_redoCheckPoints.pop();
		JournallingObject *jo = m_joIDs[c.joID];

		if( jo )
		{
			DataFile curState( DataFile::JournalData );
			jo->saveState( curState, curState.content() );
			m_undoCheckPoints.push( CheckPoint( c.joID, curState ) );

			bool prev = isJournalling();
			setJournalling( false );
			jo->restoreState( c.data.content().firstChildElement() );
			setJournalling( prev );
			Engine::getSong()->setModified();
			break;
		}
	}
}

bool ProjectJournal::canUndo() const
{
	return !m_undoCheckPoints.isEmpty();
}

bool ProjectJournal::canRedo() const
{
	return !m_redoCheckPoints.isEmpty();
}

void ProjectJournal::addJournalCheckPoint( JournallingObject *jo )
{
	if( isJournalling() )
	{
		m_redoCheckPoints.clear();

		DataFile dataFile( DataFile::JournalData );
		jo->saveState( dataFile, dataFile.content() );

		m_undoCheckPoints.push( CheckPoint( jo->id(), dataFile ) );
		if( m_undoCheckPoints.size() > MAX_UNDO_STATES )
		{
			m_undoCheckPoints.remove( 0, m_undoCheckPoints.size() - MAX_UNDO_STATES );
		}
	}
}

jo_id_t ProjectJournal::allocID( JournallingObject * _obj )
{
	jo_id_t id;
	for( jo_id_t tid = rand(); m_joIDs.contains( id = tid % EO_ID_MSB
							| EO_ID_MSB ); tid++ )
	{
	}

	m_joIDs[id] = _obj;
	//printf("new id: %d\n", id );
	return id;
}

void ProjectJournal::reallocID( const jo_id_t _id, JournallingObject * _obj )
{
	//printf("realloc %d %d\n", _id, _obj );
//	if( m_joIDs.contains( _id ) )
	{
		m_joIDs[_id] = _obj;
	}
}

jo_id_t ProjectJournal::idToSave( jo_id_t id )
{
	return id & ~EO_ID_MSB;
}

jo_id_t ProjectJournal::idFromSave( jo_id_t id )
{
	return id | EO_ID_MSB;
}

void ProjectJournal::clearJournal()
{
	m_undoCheckPoints.clear();
	m_redoCheckPoints.clear();

	for( JoIdMap::Iterator it = m_joIDs.begin(); it != m_joIDs.end(); )
	{
		if( it.value() == NULL )
		{
			it = m_joIDs.erase( it );
		}
		else
		{
			++it;
		}
	}
}

void ProjectJournal::stopAllJournalling()
{
	for( JoIdMap::Iterator it = m_joIDs.begin(); it != m_joIDs.end(); ++it)
	{
		if( it.value() != NULL )
		{
			it.value()->setJournalling(false);
		}
	}
	setJournalling(false);
}


code:
/*
 * ProjectRenderer.cpp - ProjectRenderer-class for easily rendering projects
 *
 * 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 <QFile>

#include "ProjectRenderer.h"
#include "Song.h"
#include "PerfLog.h"

#include "AudioFileWave.h"
#include "AudioFileOgg.h"
#include "AudioFileMP3.h"
#include "AudioFileFlac.h"

#ifdef LMMS_HAVE_SCHED_H
#include "sched.h"
#endif

const ProjectRenderer::FileEncodeDevice ProjectRenderer::fileEncodeDevices[] =
{

	{ ProjectRenderer::WaveFile,
		QT_TRANSLATE_NOOP( "ProjectRenderer", "WAV (*.wav)" ),
					".wav", &AudioFileWave::getInst },
	{ ProjectRenderer::FlacFile,
		QT_TRANSLATE_NOOP("ProjectRenderer", "FLAC (*.flac)"),
		".flac",
		&AudioFileFlac::getInst
	},
	{ ProjectRenderer::OggFile,
		QT_TRANSLATE_NOOP( "ProjectRenderer", "OGG (*.ogg)" ),
					".ogg",
#ifdef LMMS_HAVE_OGGVORBIS
					&AudioFileOgg::getInst
#else
					NULL
#endif
									},
	{ ProjectRenderer::MP3File,
		QT_TRANSLATE_NOOP( "ProjectRenderer", "MP3 (*.mp3)" ),
					".mp3",
#ifdef LMMS_HAVE_MP3LAME
					&AudioFileMP3::getInst
#else
					NULL
#endif
									},
	// Insert your own file-encoder infos here.
	// Maybe one day the user can add own encoders inside the program.

	{ ProjectRenderer::NumFileFormats, NULL, NULL, NULL }

} ;

ProjectRenderer::ProjectRenderer( const Mixer::qualitySettings & qualitySettings,
					const OutputSettings & outputSettings,
					ExportFileFormats exportFileFormat,
					const QString & outputFilename ) :
	QThread( Engine::mixer() ),
	m_fileDev( NULL ),
	m_qualitySettings( qualitySettings ),
	m_progress( 0 ),
	m_abort( false )
{
	AudioFileDeviceInstantiaton audioEncoderFactory = fileEncodeDevices[exportFileFormat].m_getDevInst;

	if (audioEncoderFactory)
	{
		bool successful = false;

		m_fileDev = audioEncoderFactory(
					outputFilename, outputSettings, DEFAULT_CHANNELS,
					Engine::mixer(), successful );
		if( !successful )
		{
			delete m_fileDev;
			m_fileDev = NULL;
		}
	}
}

ProjectRenderer::~ProjectRenderer()
{
}

// Little help function for getting file format from a file extension
// (only for registered file-encoders).
ProjectRenderer::ExportFileFormats ProjectRenderer::getFileFormatFromExtension(
							const QString & _ext )
{
	int idx = 0;
	while( fileEncodeDevices[idx].m_fileFormat != NumFileFormats )
	{
		if( QString( fileEncodeDevices[idx].m_extension ) == _ext )
		{
			return( fileEncodeDevices[idx].m_fileFormat );
		}
		++idx;
	}

	return( WaveFile ); // Default.
}

QString ProjectRenderer::getFileExtensionFromFormat(
		ExportFileFormats fmt )
{
	return fileEncodeDevices[fmt].m_extension;
}

void ProjectRenderer::startProcessing()
{

	if( isReady() )
	{
		// Have to do mixer stuff with GUI-thread affinity in order to
		// make slots connected to sampleRateChanged()-signals being called immediately.
		Engine::mixer()->setAudioDevice( m_fileDev,
						m_qualitySettings, false, false );

		start(
#ifndef LMMS_BUILD_WIN32
			QThread::HighPriority
#endif
						);

	}
}

void ProjectRenderer::run()
{
	MemoryManager::ThreadGuard mmThreadGuard; Q_UNUSED(mmThreadGuard);
#if 0
#if defined(LMMS_BUILD_LINUX) || defined(LMMS_BUILD_FREEBSD)
#ifdef LMMS_HAVE_SCHED_H
	cpu_set_t mask;
	CPU_ZERO( &mask );
	CPU_SET( 0, &mask );
	sched_setaffinity( 0, sizeof( mask ), &mask );
#endif
#endif
#endif

	PerfLogTimer perfLog("Project Render");

	Engine::getSong()->startExport();
	// Skip first empty buffer.
	Engine::mixer()->nextBuffer();

	m_progress = 0;

	// Now start processing
	Engine::mixer()->startProcessing(false);

	// Continually track and emit progress percentage to listeners.
	while (!Engine::getSong()->isExportDone() && !m_abort)
	{
		m_fileDev->processNextBuffer();
		const int nprog = Engine::getSong()->getExportProgress();
		if (m_progress != nprog)
		{
			m_progress = nprog;
			emit progressChanged( m_progress );
		}
	}

	// Notify mixer of the end of processing.
	Engine::mixer()->stopProcessing();

	Engine::getSong()->stopExport();

	perfLog.end();

	// If the user aborted export-process, the file has to be deleted.
	const QString f = m_fileDev->outputFile();
	if( m_abort )
	{
		QFile( f ).remove();
	}
}

void ProjectRenderer::abortProcessing()
{
	m_abort = true;
	wait();
}

void ProjectRenderer::updateConsoleProgress()
{
	const int cols = 50;
	static int rot = 0;
	char buf[80];
	char prog[cols+1];

	for( int i = 0; i < cols; ++i )
	{
		prog[i] = ( i*100/cols <= m_progress ? '-' : ' ' );
	}
	prog[cols] = 0;

	const char * activity = (const char *) "|/-\\";
	memset( buf, 0, sizeof( buf ) );
	sprintf( buf, "\r|%s|    %3d%%   %c  ", prog, m_progress,
							activity[rot] );
	rot = ( rot+1 ) % 4;

	fprintf( stderr, "%s", buf );
	fflush( stderr );
}


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

#include "ProjectVersion.h"

ProjectVersion::ProjectVersion(QString version, CompareType c) :
	m_version(version),
	m_compareType(c)
{
	// Version numbers may have build data, prefixed with a '+',
	// but this mustn't affect version precedence in comparisons
	QString metadataStripped = version.split("+").first();
	// They must have an obligatory initial segement, and may have
	// optional identifiers prefaced by a '-'. Both parts affect precedence
	QString obligatorySegment = metadataStripped.section('-', 0, 0);
	QString prereleaseSegment = metadataStripped.section('-', 1);

	// The obligatory segment consists of three identifiers: MAJOR.MINOR.PATCH
	QStringList mainVersion = obligatorySegment.split(".");
	// HACK: Pad invalid versions in order to prevent crashes
	while (mainVersion.size() < 3){ mainVersion.append("0"); }
	m_major = mainVersion.at(0).toInt();
	m_minor = mainVersion.at(1).toInt();
	m_patch = mainVersion.at(2).toInt();

	// Any # of optional pre-release identifiers may follow, separated by '.'s
	if (!prereleaseSegment.isEmpty()){ m_labels = prereleaseSegment.split("."); }

	// HACK: Handle old (1.2.2 and earlier), non-standard versions of the form
	// MAJOR.MINOR.PATCH.COMMITS, used for non-release builds from source.
	if (mainVersion.size() >= 4 && m_major <= 1 && m_minor <= 2 && m_patch <= 2){
		// Drop the standard version identifiers. erase(a, b) removes [a,b)
		mainVersion.erase(mainVersion.begin(), mainVersion.begin() + 3);
		// Prepend the remaining identifiers as prerelease versions
		m_labels = mainVersion + m_labels;
		// Bump the patch version. x.y.z-a < x.y.z, but we want x.y.z.a > x.y.z
		m_patch += 1;
	}
}

ProjectVersion::ProjectVersion(const char* version, CompareType c) : ProjectVersion(QString(version), c)
{
}

//! @param c Determines the number of identifiers to check when comparing
int ProjectVersion::compare(const ProjectVersion & a, const ProjectVersion & b, CompareType c)
{
	// How many identifiers to compare before we consider the versions equal
	const int limit = static_cast<int>(c);

	// Use the value of limit to zero out identifiers we don't care about
	int aMaj = 0, bMaj = 0, aMin = 0, bMin = 0, aPat = 0, bPat = 0;
	if (limit >= 1){ aMaj = a.getMajor(); bMaj = b.getMajor(); }
	if (limit >= 2){ aMin = a.getMinor(); bMin = b.getMinor(); }
	if (limit >= 3){ aPat = a.getPatch(); bPat = b.getPatch(); }

	// Then we can compare as if we care about every identifier
	if(aMaj != bMaj){ return aMaj - bMaj; }
	if(aMin != bMin){ return aMin - bMin; }
	if(aPat != bPat){ return aPat - bPat; }

	// Decide how many optional identifiers we care about
	const int maxLabels = qMax(0, limit - 3);
	const auto aLabels = a.getLabels().mid(0, maxLabels);
	const auto bLabels = b.getLabels().mid(0, maxLabels);

	// We can only compare identifiers if both versions have them
	const int commonLabels = qMin(aLabels.size(), bLabels.size());
	// If one version has optional labels and the other doesn't,
	// the one without them is bigger
	if (commonLabels == 0){ return bLabels.size() - aLabels.size(); }

	// Otherwise, compare as many labels as we can
	for (int i = 0; i < commonLabels; i++){
		const QString& labelA = aLabels.at(i);
		const QString& labelB = bLabels.at(i);
		// If both labels are the same, skip
		if (labelA == labelB){ continue; }
		// Numeric and non-numeric identifiers compare differently
		bool aIsNumeric = false, bIsNumeric = false;
		const int numA = labelA.toInt(&aIsNumeric);
		const int numB = labelB.toInt(&bIsNumeric);
		// toInt reads '-x' as a negative number, semver says it's non-numeric
		aIsNumeric &= !labelA.startsWith("-");
		bIsNumeric &= !labelB.startsWith("-");
		// If only one identifier is numeric, that one is smaller
		if (aIsNumeric != bIsNumeric){ return aIsNumeric ? -1 : 1; }
		// If both are numeric, compare as numbers
		if (aIsNumeric && bIsNumeric){ return numA - numB; }
		// Otherwise, compare lexically
		return labelA.compare(labelB);
	}

	// If everything else matches, the version with more labels is bigger
	return aLabels.size() - bLabels.size();
}

int ProjectVersion::compare(ProjectVersion v1, ProjectVersion v2)
{
	return compare(v1, v2, std::min(v1.getCompareType(), v2.getCompareType()));
}


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

#define COMPILE_REMOTE_PLUGIN_BASE
//#define DEBUG_REMOTE_PLUGIN
#ifdef DEBUG_REMOTE_PLUGIN
#include <QDebug>
#endif

#include "BufferManager.h"
#include "RemotePlugin.h"
#include "Mixer.h"
#include "Engine.h"

#include <QDebug>
#include <QDir>

#ifndef SYNC_WITH_SHM_FIFO
#include <QtCore/QUuid>
#include <sys/socket.h>
#include <sys/un.h>
#endif

// simple helper thread monitoring our RemotePlugin - if process terminates
// unexpectedly invalidate plugin so LMMS doesn't lock up
ProcessWatcher::ProcessWatcher( RemotePlugin * _p ) :
	QThread(),
	m_plugin( _p ),
	m_quit( false )
{
}

void ProcessWatcher::run()
{
	m_plugin->m_process.start( m_plugin->m_exec, m_plugin->m_args );
	exec();
	m_plugin->m_process.moveToThread( m_plugin->thread() );
	while( !m_quit && m_plugin->messagesLeft() )
	{
		msleep( 200 );
	}
	if( !m_quit )
	{
		fprintf( stderr,
				"remote plugin died! invalidating now.\n" );

		m_plugin->invalidate();
	}
}

RemotePlugin::RemotePlugin() :
	QObject(),
#ifdef SYNC_WITH_SHM_FIFO
	RemotePluginBase( new shmFifo(), new shmFifo() ),
#else
	RemotePluginBase(),
#endif
	m_failed( true ),
	m_watcher( this ),
	m_commMutex( QMutex::Recursive ),
	m_splitChannels( false ),
#ifdef USE_QT_SHMEM
	m_shmObj(),
#else
	m_shmID( 0 ),
#endif
	m_shmSize( 0 ),
	m_shm( NULL ),
	m_inputCount( DEFAULT_CHANNELS ),
	m_outputCount( DEFAULT_CHANNELS )
{
#ifndef SYNC_WITH_SHM_FIFO
	struct sockaddr_un sa;
	sa.sun_family = AF_LOCAL;

	m_socketFile = QDir::tempPath() + QDir::separator() +
						QUuid::createUuid().toString();
	auto path = m_socketFile.toUtf8();
	size_t length = path.length();
	if ( length >= sizeof sa.sun_path )
	{
		length = sizeof sa.sun_path - 1;
		qWarning( "Socket path too long." );
	}
	memcpy(sa.sun_path, path.constData(), length );
	sa.sun_path[length] = '\0';

	m_server = socket( PF_LOCAL, SOCK_STREAM, 0 );
	if ( m_server == -1 )
	{
		qWarning( "Unable to start the server." );
	}
	remove(path.constData());
	int ret = bind( m_server, (struct sockaddr *) &sa, sizeof sa );
	if ( ret == -1 || listen( m_server, 1 ) == -1 )
	{
		qWarning( "Unable to start the server." );
	}
#endif

	connect( &m_process, SIGNAL( finished( int, QProcess::ExitStatus ) ),
		this, SLOT( processFinished( int, QProcess::ExitStatus ) ),
		Qt::DirectConnection );
	connect( &m_process, SIGNAL( errorOccurred( QProcess::ProcessError ) ),
			 this, SLOT( processErrored( QProcess::ProcessError ) ),
		Qt::DirectConnection );
	connect( &m_process, SIGNAL( finished( int, QProcess::ExitStatus ) ),
		&m_watcher, SLOT( quit() ), Qt::DirectConnection );
}

RemotePlugin::~RemotePlugin()
{
	m_watcher.stop();
	m_watcher.wait();

	if( m_failed == false )
	{
		if( isRunning() )
		{
			lock();
			sendMessage( IdQuit );

			m_process.waitForFinished( 1000 );
			if( m_process.state() != QProcess::NotRunning )
			{
				m_process.terminate();
				m_process.kill();
			}
			unlock();
		}

#ifndef USE_QT_SHMEM
		shmdt( m_shm );
		shmctl( m_shmID, IPC_RMID, NULL );
#endif
	}

#ifndef SYNC_WITH_SHM_FIFO
	if ( close( m_server ) == -1)
	{
		qWarning( "Error freeing resources." );
	}
	remove( m_socketFile.toUtf8().constData() );
#endif
}

bool RemotePlugin::init(const QString &pluginExecutable,
							bool waitForInitDoneMsg , QStringList extraArgs)
{
	lock();
	if( m_failed )
	{
#ifdef SYNC_WITH_SHM_FIFO
		reset( new shmFifo(), new shmFifo() );
#endif
		m_failed = false;
	}
	QString exec = QFileInfo(QDir("plugins:"), pluginExecutable).absoluteFilePath();
#ifdef LMMS_BUILD_APPLE
	// search current directory first
	QString curDir = QCoreApplication::applicationDirPath() + "/" + pluginExecutable;
	if( QFile( curDir ).exists() )
	{
		exec = curDir;
	}
#endif
#ifdef LMMS_BUILD_WIN32
	if( ! exec.endsWith( ".exe", Qt::CaseInsensitive ) )
	{
		exec += ".exe";
	}
#endif

	if( ! QFile( exec ).exists() )
	{
		qWarning( "Remote plugin '%s' not found.",
						exec.toUtf8().constData() );
		m_failed = true;
		invalidate();
		unlock();
		return failed();
	}

	// ensure the watcher is ready in case we're running again
	// (e.g. 32-bit VST plugins on Windows)
	m_watcher.wait();
	m_watcher.reset();

	QStringList args;
#ifdef SYNC_WITH_SHM_FIFO
	// swap in and out for bidirectional communication
	args << QString::number( out()->shmKey() );
	args << QString::number( in()->shmKey() );
#else
	args << m_socketFile;
#endif
	args << extraArgs;
#ifndef DEBUG_REMOTE_PLUGIN
	m_process.setProcessChannelMode( QProcess::ForwardedChannels );
	m_process.setWorkingDirectory( QCoreApplication::applicationDirPath() );
	m_exec = exec;
	m_args = args;
	// we start the process on the watcher thread to work around QTBUG-8819
	m_process.moveToThread( &m_watcher );
	m_watcher.start( QThread::LowestPriority );
#else
	qDebug() << exec << args;
#endif

#ifndef SYNC_WITH_SHM_FIFO
	struct pollfd pollin;
	pollin.fd = m_server;
	pollin.events = POLLIN;

	switch ( poll( &pollin, 1, 30000 ) )
	{
		case -1:
			qWarning( "Unexpected poll error." );
			break;

		case 0:
			qWarning( "Remote plugin did not connect." );
			break;

		default:
			m_socket = accept( m_server, NULL, NULL );
			if ( m_socket == -1 )
			{
				qWarning( "Unexpected socket error." );
			}
	}
#endif

	resizeSharedProcessingMemory();

	if( waitForInitDoneMsg )
	{
		waitForInitDone();
	}
	unlock();

	return failed();
}

bool RemotePlugin::process( const sampleFrame * _in_buf,
						sampleFrame * _out_buf )
{
	const fpp_t frames = Engine::mixer()->framesPerPeriod();

	if( m_failed || !isRunning() )
	{
		if( _out_buf != NULL )
		{
			BufferManager::clear( _out_buf, frames );
		}
		return false;
	}

	if( m_shm == NULL )
	{
		// m_shm being zero means we didn't initialize everything so
		// far so process one message each time (and hope we get
		// information like SHM-key etc.) until we process messages
		// in a later stage of this procedure
		if( m_shmSize == 0 )
		{
			lock();
			fetchAndProcessAllMessages();
			unlock();
		}
		if( _out_buf != NULL )
		{
			BufferManager::clear( _out_buf, frames );
		}
		return false;
	}

	memset( m_shm, 0, m_shmSize );

	ch_cnt_t inputs = qMin<ch_cnt_t>( m_inputCount, DEFAULT_CHANNELS );

	if( _in_buf != NULL && inputs > 0 )
	{
		if( m_splitChannels )
		{
			for( ch_cnt_t ch = 0; ch < inputs; ++ch )
			{
				for( fpp_t frame = 0; frame < frames; ++frame )
				{
					m_shm[ch * frames + frame] =
							_in_buf[frame][ch];
				}
			}
		}
		else if( inputs == DEFAULT_CHANNELS )
		{
			memcpy( m_shm, _in_buf, frames * BYTES_PER_FRAME );
		}
		else
		{
			sampleFrame * o = (sampleFrame *) m_shm;
			for( ch_cnt_t ch = 0; ch < inputs; ++ch )
			{
				for( fpp_t frame = 0; frame < frames; ++frame )
				{
					o[frame][ch] = _in_buf[frame][ch];
				}
			}
		}
	}

	lock();
	sendMessage( IdStartProcessing );

	if( m_failed || _out_buf == NULL || m_outputCount == 0 )
	{
		unlock();
		return false;
	}

	waitForMessage( IdProcessingDone );
	unlock();

	const ch_cnt_t outputs = qMin<ch_cnt_t>( m_outputCount,
							DEFAULT_CHANNELS );
	if( m_splitChannels )
	{
		for( ch_cnt_t ch = 0; ch < outputs; ++ch )
		{
			for( fpp_t frame = 0; frame < frames; ++frame )
			{
				_out_buf[frame][ch] = m_shm[( m_inputCount+ch )*
								frames + frame];
			}
		}
	}
	else if( outputs == DEFAULT_CHANNELS )
	{
		memcpy( _out_buf, m_shm + m_inputCount * frames,
						frames * BYTES_PER_FRAME );
	}
	else
	{
		sampleFrame * o = (sampleFrame *) ( m_shm +
							m_inputCount*frames );
		// clear buffer, if plugin didn't fill up both channels
		BufferManager::clear( _out_buf, frames );

		for( ch_cnt_t ch = 0; ch <
				qMin<int>( DEFAULT_CHANNELS, outputs ); ++ch )
		{
			for( fpp_t frame = 0; frame < frames; ++frame )
			{
				_out_buf[frame][ch] = o[frame][ch];
			}
		}
	}

	return true;
}

void RemotePlugin::processMidiEvent( const MidiEvent & _e,
							const f_cnt_t _offset )
{
	message m( IdMidiEvent );
	m.addInt( _e.type() );
	m.addInt( _e.channel() );
	m.addInt( _e.param( 0 ) );
	m.addInt( _e.param( 1 ) );
	m.addInt( _offset );
	lock();
	sendMessage( m );
	unlock();
}

void RemotePlugin::showUI()
{
	lock();
	sendMessage( IdShowUI );
	unlock();
}

void RemotePlugin::hideUI()
{
	lock();
	sendMessage( IdHideUI );
	unlock();
}

void RemotePlugin::resizeSharedProcessingMemory()
{
	const size_t s = ( m_inputCount+m_outputCount ) *
				Engine::mixer()->framesPerPeriod() *
							sizeof( float );
	if( m_shm != NULL )
	{
#ifdef USE_QT_SHMEM
		m_shmObj.detach();
#else
		shmdt( m_shm );
		shmctl( m_shmID, IPC_RMID, NULL );
#endif
	}

	static int shm_key = 0;
#ifdef USE_QT_SHMEM
	do
	{
		m_shmObj.setKey( QString( "%1" ).arg( ++shm_key ) );
		m_shmObj.create( s );
	} while( m_shmObj.error() != QSharedMemory::NoError );

	m_shm = (float *) m_shmObj.data();
#else
	while( ( m_shmID = shmget( ++shm_key, s, IPC_CREAT | IPC_EXCL |
								0600 ) ) == -1 )
	{
	}

	m_shm = (float *) shmat( m_shmID, 0, 0 );
#endif
	m_shmSize = s;
	sendMessage( message( IdChangeSharedMemoryKey ).
				addInt( shm_key ).addInt( m_shmSize ) );
}

void RemotePlugin::processFinished( int exitCode,
					QProcess::ExitStatus exitStatus )
{
	if ( exitStatus == QProcess::CrashExit )
	{
		qCritical() << "Remote plugin crashed";
	}
	else if ( exitCode )
	{
		qCritical() << "Remote plugin exit code: " << exitCode;
	}
#ifndef SYNC_WITH_SHM_FIFO
	invalidate();
#endif
}

void RemotePlugin::processErrored( QProcess::ProcessError err )
{
	qCritical() << "Process error: " << err;
}

bool RemotePlugin::processMessage( const message & _m )
{
	lock();
	message reply_message( _m.id );
	bool reply = false;
	switch( _m.id )
	{
		case IdUndefined:
			unlock();
			return false;

		case IdInitDone:
			reply = true;
			break;

		case IdSampleRateInformation:
			reply = true;
			reply_message.addInt( Engine::mixer()->processingSampleRate() );
			break;

		case IdBufferSizeInformation:
			reply = true;
			reply_message.addInt( Engine::mixer()->framesPerPeriod() );
			break;

		case IdChangeInputCount:
			m_inputCount = _m.getInt( 0 );
			resizeSharedProcessingMemory();
			break;

		case IdChangeOutputCount:
			m_outputCount = _m.getInt( 0 );
			resizeSharedProcessingMemory();
			break;

		case IdChangeInputOutputCount:
			m_inputCount = _m.getInt( 0 );
			m_outputCount = _m.getInt( 1 );
			resizeSharedProcessingMemory();
			break;

		case IdDebugMessage:
			fprintf( stderr, "RemotePlugin::DebugMessage: %s",
						_m.getString( 0 ).c_str() );
			break;

		case IdProcessingDone:
		case IdQuit:
		default:
			break;
	}
	if( reply )
	{
		sendMessage( reply_message );
	}
	unlock();

	return true;
}