/*
 * mixer.cpp - audio-device-independent mixer for LMMS
 *
 * Copyright (c) 2004-2009 Tobias Doerffel <tobydox/at/users.sourceforge.net>
 * 
 * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
 *
 * 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 <math.h>

#include "mixer.h"
#include "fx_mixer.h"
#include "play_handle.h"
#include "effect.h"
#include "song.h"
#include "templates.h"
#include "envelope_and_lfo_parameters.h"
#include "note_play_handle.h"
#include "instrument_track.h"
#include "debug.h"
#include "engine.h"
#include "config_mgr.h"
#include "audio_port.h"
#include "sample_play_handle.h"
#include "piano_roll.h"
#include "micro_timer.h"
#include "basic_ops.h"

#include "audio_device.h"
#include "midi_client.h"

// platform-specific audio-interface-classes
#include "audio_alsa.h"
#include "audio_jack.h"
#include "audio_oss.h"
#include "audio_portaudio.h"
#include "audio_pulseaudio.h"
#include "audio_sdl.h"
#include "audio_dummy.h"

// platform-specific midi-interface-classes
#include "midi_alsa_raw.h"
#include "midi_alsa_seq.h"
#include "midi_oss.h"
#include "midi_winmm.h"
#include "midi_dummy.h"

#ifdef LMMS_HAVE_PTHREAD_H
#include <pthread.h>
#endif


static QVector<fx_ch_t> __fx_channel_jobs( NumFxChannels );



class MixerWorkerThread : public QThread
{
public:
	enum JobTypes
	{
		InvalidJob,
		PlayHandle,
		AudioPortEffects,
		EffectChannel,
		NumJobTypes
	} ;

	struct JobQueueItem
	{
		JobQueueItem() :
			type( InvalidJob ),
			job( NULL ),
			param( 0 ),
			done( false )
		{
		}
		JobQueueItem( JobTypes _type, void * _job, int _param = 0 ) :
			type( _type ),
			job( _job ),
			param( _param ),
			done( false )
		{
		}

		JobTypes type;
		void * job;
		int param;

		QAtomicInt done;

	} ;

	struct JobQueue
	{
#define JOB_QUEUE_SIZE 1024
		JobQueue() :
			queueSize( 0 )
		{
		}

		JobQueueItem items[JOB_QUEUE_SIZE];
		int queueSize;
		QAtomicInt itemsDone;
	} ;

	static JobQueue s_jobQueue;

	MixerWorkerThread( int _worker_num, mixer * _mixer ) :
		QThread( _mixer ),
		m_workingBuf( alignedAllocFrames( _mixer->framesPerPeriod() ) ),
		m_workerNum( _worker_num ),
		m_quit( false ),
		m_mixer( _mixer ),
		m_queueReadyWaitCond( &m_mixer->m_queueReadyWaitCond )
	{
	}

	virtual ~MixerWorkerThread()
	{
		alignedFreeFrames( m_workingBuf );
	}

	virtual void quit( void )
	{
		m_quit = true;
	}

	void processJobQueue( void );


private:
	virtual void run( void )
	{
#if 0
#ifdef LMMS_BUILD_LINUX
#ifdef LMMS_HAVE_PTHREAD_H
		cpu_set_t mask;
		CPU_ZERO( &mask );
		CPU_SET( m_workerNum, &mask );
		pthread_setaffinity_np( pthread_self(), sizeof( mask ), &mask );
#endif
#endif
#endif
		QMutex m;
		while( m_quit == false )
		{
			m.lock();
			m_queueReadyWaitCond->wait( &m );
			processJobQueue();
			m.unlock();
		}
	}

	sampleFrame * m_workingBuf;
	int m_workerNum;
	volatile bool m_quit;
	mixer * m_mixer;
	QWaitCondition * m_queueReadyWaitCond;

} ;


MixerWorkerThread::JobQueue MixerWorkerThread::s_jobQueue;



void MixerWorkerThread::processJobQueue( void )
{
	for( int i = 0; i < s_jobQueue.queueSize; ++i )
	{
		JobQueueItem * it = &s_jobQueue.items[i];
		if( it->done.fetchAndStoreOrdered( 1 ) == 0 )
		{
			switch( it->type )
			{
				case PlayHandle:
					( (playHandle *) it->job )->
							play( m_workingBuf );
					break;
				case AudioPortEffects:
					{
	audioPort * a = (audioPort *) it->job;
	const bool me = a->processEffects();
	if( me || a->m_bufferUsage != audioPort::NoUsage )
	{
		engine::getFxMixer()->mixToChannel( a->firstBuffer(),
							a->nextFxChannel() );
		a->nextPeriod();
	}
					}
					break;
				case EffectChannel:
	engine::getFxMixer()->processChannel( (fx_ch_t) it->param );
					break;
				default:
					break;
			}
			s_jobQueue.itemsDone.fetchAndAddOrdered( 1 );
		}
	}
}

#define FILL_JOB_QUEUE_BEGIN(_vec_type,_vec,_condition)			\
	MixerWorkerThread::s_jobQueue.queueSize = 0;			\
	MixerWorkerThread::s_jobQueue.itemsDone = 0;			\
	for( _vec_type::iterator it = _vec.begin();			\
					it != _vec.end(); ++it )	\
	{								\
		if( _condition )					\
		{

#define FILL_JOB_QUEUE_END()						\
			++MixerWorkerThread::s_jobQueue.queueSize;	\
		}							\
	}

#define FILL_JOB_QUEUE(_vec_type,_vec,_job_type,_condition)		\
	FILL_JOB_QUEUE_BEGIN(_vec_type,_vec,_condition)			\
	MixerWorkerThread::s_jobQueue.items				\
		[MixerWorkerThread::s_jobQueue.queueSize] =		\
			MixerWorkerThread::JobQueueItem( _job_type,	\
							(void *) *it );	\
	FILL_JOB_QUEUE_END()

#define FILL_JOB_QUEUE_PARAM(_vec_type,_vec,_job_type,_condition)	\
	FILL_JOB_QUEUE_BEGIN(_vec_type,_vec,_condition)			\
	MixerWorkerThread::s_jobQueue.items				\
		[MixerWorkerThread::s_jobQueue.queueSize] =		\
			MixerWorkerThread::JobQueueItem( _job_type,	\
							NULL, *it );	\
	FILL_JOB_QUEUE_END()

#define START_JOBS()							\
	m_queueReadyWaitCond.wakeAll();

// define a pause instruction for spinlock-loop - merely useful on
// HyperThreading systems with just one physical core (e.g. Intel Atom)
#ifdef LMMS_HOST_X86_64
#define SPINLOCK_PAUSE() 	asm( "pause" )
#else
#ifdef LMMS_HOST_X86_64
#define SPINLOCK_PAUSE() 	asm( "pause" )
#else
#define SPINLOCK_PAUSE()
#endif
#endif

#define WAIT_FOR_JOBS()							\
	m_workers[m_numWorkers]->processJobQueue();			\
	while( MixerWorkerThread::s_jobQueue.itemsDone <		\
			MixerWorkerThread::s_jobQueue.queueSize )	\
	{								\
		SPINLOCK_PAUSE();					\
	}								\




mixer::mixer( void ) :
	m_framesPerPeriod( DEFAULT_BUFFER_SIZE ),
	m_workingBuf( NULL ),
	m_inputBufferRead( 0 ),
	m_inputBufferWrite( 1 ),
	m_readBuf( NULL ),
	m_writeBuf( NULL ),
	m_cpuLoad( 0 ),
	m_workers(),
	m_numWorkers( QThread::idealThreadCount()-1 ),
	m_queueReadyWaitCond(),
	m_qualitySettings( qualitySettings::Mode_Draft ),
	m_masterGain( 1.0f ),
	m_audioDev( NULL ),
	m_oldAudioDev( NULL ),
	m_globalMutex( QMutex::Recursive )
{
	for( int i = 0; i < 2; ++i )
	{
		m_inputBufferFrames[i] = 0;
		m_inputBufferSize[i] = DEFAULT_BUFFER_SIZE * 100;
		m_inputBuffer[i] = alignedAllocFrames( 
						DEFAULT_BUFFER_SIZE * 100 );
		clearAudioBuffer( m_inputBuffer[i], m_inputBufferSize[i] );
	}

	for( int i = 1; i < NumFxChannels+1; ++i )
	{
		__fx_channel_jobs[i-1] = (fx_ch_t) i;
	}

	// just rendering?
	if( !engine::hasGUI() )
	{
		m_framesPerPeriod = DEFAULT_BUFFER_SIZE;
		m_fifo = new fifo( 1 );
	}
	else if( configManager::inst()->value( "mixer", "framesperaudiobuffer"
						).toInt() >= 32 )
	{
		m_framesPerPeriod =
			(fpp_t) configManager::inst()->value( "mixer",
					"framesperaudiobuffer" ).toInt();

		if( m_framesPerPeriod > DEFAULT_BUFFER_SIZE )
		{
			m_fifo = new fifo( m_framesPerPeriod
							/ DEFAULT_BUFFER_SIZE );
			m_framesPerPeriod = DEFAULT_BUFFER_SIZE;
		}
		else
		{
			m_fifo = new fifo( 1 );
		}
	}
	else
	{
		configManager::inst()->setValue( "mixer",
							"framesperaudiobuffer",
				QString::number( m_framesPerPeriod ) );
		m_fifo = new fifo( 1 );
	}

	m_workingBuf = alignedAllocFrames( m_framesPerPeriod );
	for( Uint8 i = 0; i < 3; i++ )
	{
		m_readBuf = alignedAllocFrames( m_framesPerPeriod );
		clearAudioBuffer( m_readBuf, m_framesPerPeriod );
		m_bufferPool.push_back( m_readBuf );
	}

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

	m_poolDepth = 2;
	m_readBuffer = 0;
	m_writeBuffer = 1;
}




mixer::~mixer()
{
	// distribute an empty job-queue so that worker-threads
	// get out of their processing-loop
	MixerWorkerThread::s_jobQueue.queueSize = 0;
	for( int w = 0; w < m_numWorkers; ++w )
	{
		m_workers[w]->quit();
	}
	START_JOBS();
	for( int w = 0; w < m_numWorkers; ++w )
	{
		m_workers[w]->wait( 500 );
	}

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

	delete m_audioDev;
	delete m_midiClient;

	for( Uint8 i = 0; i < 3; i++ )
	{
		alignedFreeFrames( m_bufferPool[i] );
	}

	alignedFreeFrames( m_workingBuf );
}




void mixer::initDevices( void )
{
	m_audioDev = tryAudioDevices();
	m_midiClient = tryMidiClients();
}




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

	m_audioDev->startProcessing();
}




void mixer::stopProcessing( void )
{
	if( m_fifoWriter != NULL )
	{
		m_fifoWriter->finish();
		m_audioDev->stopProcessing();
		m_fifoWriter->wait( 1000 );
		m_fifoWriter->terminate();
		delete m_fifoWriter;
		m_fifoWriter = NULL;
	}
	else
	{
		m_audioDev->stopProcessing();
	}
}




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




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




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




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




bool mixer::criticalXRuns( void ) const
{
	return m_cpuLoad >= 99 && engine::getSong()->realTimeTask() == true;
}




void mixer::pushInputFrames( sampleFrame * _ab, const f_cnt_t _frames )
{
	lockInputFrames();

	f_cnt_t frames = m_inputBufferFrames[ m_inputBufferWrite ];
	int size = m_inputBufferSize[ m_inputBufferWrite ];
	sampleFrame * buf = m_inputBuffer[ m_inputBufferWrite ];
	
	if( frames + _frames > size )
	{
		size = qMax( size * 2, frames + _frames );
		sampleFrame * ab = alignedAllocFrames( size );
		alignedMemCpy( ab, buf, frames * sizeof( sampleFrame ) );
		alignedFreeFrames( buf );

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

		buf = ab;
	}
	
	alignedMemCpy( &buf[ frames ], _ab, _frames * sizeof( sampleFrame ) );
	m_inputBufferFrames[ m_inputBufferWrite ] += _frames;
	
	unlockInputFrames();
}




sampleFrameA * mixer::renderNextBuffer( void )
{
	microTimer timer;
	static song::playPos last_metro_pos = -1;

	song::playPos p = engine::getSong()->getPlayPos(
						song::Mode_PlayPattern );
	if( engine::getSong()->playMode() == song::Mode_PlayPattern &&
		engine::getPianoRoll()->isRecording() == true &&
		p != last_metro_pos && p.getTicks() %
					(DefaultTicksPerTact / 4 ) == 0 )
	{
		addPlayHandle( new samplePlayHandle( "misc/metronome01.ogg" ) );
		last_metro_pos = p;
	}

	lockInputFrames();
	// swap buffer
	m_inputBufferWrite = ( m_inputBufferWrite + 1 ) % 2;
	m_inputBufferRead =  ( m_inputBufferRead + 1 ) % 2;
	// clear new write buffer
	m_inputBufferFrames[ m_inputBufferWrite ] = 0;
	unlockInputFrames();


	// now we have to make sure no other thread does anything bad
	// while we're acting...
	lock();

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

		if( it != m_playHandles.end() )
		{
			delete *it;
			m_playHandles.erase( it );
		}

		it_rem = m_playHandlesToRemove.erase( it_rem );
	}

	// rotate buffers
	m_writeBuffer = ( m_writeBuffer + 1 ) % m_poolDepth;
	m_readBuffer = ( m_readBuffer + 1 ) % m_poolDepth;

	m_writeBuf = m_bufferPool[m_writeBuffer];
	m_readBuf = m_bufferPool[m_readBuffer];

	// clear last audio-buffer
	clearAudioBuffer( m_writeBuf, m_framesPerPeriod );

	// prepare master mix (clear internal buffers etc.)
	engine::getFxMixer()->prepareMasterMix();

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


	// STAGE 1: run and render all play handles
	FILL_JOB_QUEUE(playHandleVector,m_playHandles,
					MixerWorkerThread::PlayHandle,
					!( *it )->done());
	START_JOBS();
	WAIT_FOR_JOBS();

	// removed all play handles which are done
	for( playHandleVector::iterator it = m_playHandles.begin();
						it != m_playHandles.end(); )
	{
		if( ( *it )->affinityMatters() &&
			( *it )->affinity() != QThread::currentThread() )
		{
			++it;
			continue;
		}
		if( ( *it )->done() )
		{
			delete *it;
			it = m_playHandles.erase( it );
		}
		else
		{
			++it;
		}
	}


	// STAGE 2: process effects of all instrument- and sampletracks
	FILL_JOB_QUEUE(QVector<audioPort*>,m_audioPorts,
					MixerWorkerThread::AudioPortEffects,1);
	START_JOBS();
	WAIT_FOR_JOBS();


	// STAGE 3: process effects in FX mixer
	FILL_JOB_QUEUE_PARAM(QVector<fx_ch_t>,__fx_channel_jobs,
					MixerWorkerThread::EffectChannel,1);
	START_JOBS();
	WAIT_FOR_JOBS();


	// STAGE 4: do master mix in FX mixer
	engine::getFxMixer()->masterMix( m_writeBuf );

	unlock();


	emit nextAudioBuffer();

	// and trigger LFOs
	envelopeAndLFOParameters::triggerLFO();
	controller::triggerFrameCounter();

	const float new_cpu_load = timer.elapsed() / 10000.0f *
				processingSampleRate() / m_framesPerPeriod;
	m_cpuLoad = tLimit( (int) ( new_cpu_load * 0.1f + m_cpuLoad * 0.9f ), 0,
									100 );

	return m_readBuf;
}




// removes all play-handles. this is neccessary, when the song is stopped ->
// all remaining notes etc. would be played until their end
void mixer::clear( void )
{
	// TODO: m_midiClient->noteOffAll();
	lock();
	for( playHandleVector::iterator it = m_playHandles.begin();
					it != m_playHandles.end(); ++it )
	{
		// we must not delete instrument-play-handles as they exist
		// during the whole lifetime of an instrument
		if( ( *it )->type() != playHandle::InstrumentPlayHandle )
		{
			m_playHandlesToRemove.push_back( *it );
		}
	}
	unlock();
}




void mixer::bufferToPort( const sampleFrame * _buf,
					const fpp_t _frames,
					const f_cnt_t _offset,
					stereoVolumeVector _vv,
						audioPort * _port )
{
	const int start_frame = _offset % m_framesPerPeriod;
	int end_frame = start_frame + _frames;
	const int loop1_frame = qMin<int>( end_frame, m_framesPerPeriod );

	_port->lockFirstBuffer();
	unalignedBufMixLRCoeff( _port->firstBuffer() + start_frame,
					_buf, _vv.vol[0], _vv.vol[1],
						loop1_frame - start_frame );
	_port->unlockFirstBuffer();

	_port->lockSecondBuffer();
	if( end_frame > m_framesPerPeriod )
	{
		const int frames_done = m_framesPerPeriod - start_frame;
		end_frame -= m_framesPerPeriod;
		end_frame = qMin<int>( end_frame, m_framesPerPeriod );
		unalignedBufMixLRCoeff( _port->secondBuffer(),
						_buf+frames_done,
						_vv.vol[0], _vv.vol[1],
						end_frame );
		// we used both buffers so set flags
		_port->m_bufferUsage = audioPort::BothBuffers;
	}
	else if( _port->m_bufferUsage == audioPort::NoUsage )
	{
		// only first buffer touched
		_port->m_bufferUsage = audioPort::FirstBuffer;
	}
	_port->unlockSecondBuffer();
}




void mixer::clearAudioBuffer( sampleFrame * _ab, const f_cnt_t _frames,
							const f_cnt_t _offset )
{
	if( likely( (size_t)( _ab+_offset ) % 16 == 0 && _frames % 8 == 0 ) )
	{
		alignedMemClear( _ab+_offset, sizeof( *_ab ) * _frames );
	}
	else
	{
		memset( _ab+_offset, 0, sizeof( *_ab ) * _frames );
	}
}



#ifndef LMMS_DISABLE_SURROUND
void mixer::clearAudioBuffer( surroundSampleFrame * _ab, const f_cnt_t _frames,
							const f_cnt_t _offset )
{
	memset( _ab+_offset, 0, sizeof( *_ab ) * _frames );
}
#endif




float mixer::peakValueLeft( sampleFrame * _ab, const f_cnt_t _frames )
{
	float p = 0.0f;
	for( f_cnt_t f = 0; f < _frames; ++f )
	{
		if( _ab[f][0] > p )
		{
			p = _ab[f][0];
		}
		else if( -_ab[f][0] > p )
		{
			p = -_ab[f][0];
		}
	}
	return p;
}




float mixer::peakValueRight( sampleFrame * _ab, const f_cnt_t _frames )
{
	float p = 0.0f;
	for( f_cnt_t f = 0; f < _frames; ++f )
	{
		if( _ab[f][1] > p )
		{
			p = _ab[f][1];
		}
		else if( -_ab[f][1] > p )
		{
			p = -_ab[f][1];
		}
	}
	return p;
}




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

	m_qualitySettings = _qs;
	m_audioDev->applyQualitySettings();

	emit sampleRateChanged();
	emit qualitySettingsChanged();

	startProcessing();
}




void mixer::setAudioDevice( audioDevice * _dev )
{
	stopProcessing();

	m_oldAudioDev = m_audioDev;

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

	emit sampleRateChanged();

	startProcessing();
}




void mixer::setAudioDevice( audioDevice * _dev,
				const struct qualitySettings & _qs,
				bool _needs_fifo )
{
	// don't delete the audio-device
	stopProcessing();

	m_qualitySettings = _qs;
	m_oldAudioDev = m_audioDev;

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

	emit qualitySettingsChanged();
	emit sampleRateChanged();

	startProcessing( _needs_fifo );
}




void mixer::restoreAudioDevice( void )
{
	if( m_oldAudioDev != NULL )
	{
		stopProcessing();
		delete m_audioDev;

		m_audioDev = m_oldAudioDev;
		emit sampleRateChanged();

		m_oldAudioDev = NULL;
		startProcessing();
	}
}




void mixer::removeAudioPort( audioPort * _port )
{
	QVector<audioPort *>::iterator it = qFind( m_audioPorts.begin(),
							m_audioPorts.end(),
							_port );
	if( it != m_audioPorts.end() )
	{
		lock();
		m_audioPorts.erase( it );
		unlock();
	}
}




void mixer::removePlayHandle( playHandle * _ph )
{
	lock();
	// check thread affinity as we must not delete play-handles
	// which were created in a thread different than mixer thread
	if( _ph->affinityMatters() &&
				_ph->affinity() == QThread::currentThread() )
	{
		playHandleVector::iterator it =
				qFind( m_playHandles.begin(),
						m_playHandles.end(), _ph );
		if( it != m_playHandles.end() )
		{
			m_playHandles.erase( it );
			delete _ph;
		}
	}
	else
	{
		m_playHandlesToRemove.push_back( _ph );
	}
	unlock();
}




void mixer::removePlayHandles( track * _track )
{
	lock();
	playHandleVector::iterator it = m_playHandles.begin();
	while( it != m_playHandles.end() )
	{
		if( ( *it )->isFromTrack( _track ) )
		{
			delete *it;
			it = m_playHandles.erase( it );
		}
		else
		{
			++it;
		}
	}
	unlock();
}




audioDevice * mixer::tryAudioDevices( void )
{
	bool success_ful = false;
	audioDevice * dev = NULL;
	QString dev_name = configManager::inst()->value( "mixer", "audiodev" );

	if( dev_name == audioDummy::name() )
	{
		dev_name = "";
	}

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


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


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


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


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


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

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

	printf( "No audio-driver working - falling back to dummy-audio-"
		"driver\nYou can render your songs and listen to the output "
		"files...\n" );

	m_audioDevName = audioDummy::name();

	return new audioDummy( success_ful, this );
}




midiClient * mixer::tryMidiClients( void )
{
	QString client_name = configManager::inst()->value( "mixer",
								"mididev" );

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

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

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

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

	printf( "Couldn't create MIDI-client, neither with ALSA nor with "
		"OSS. Will use dummy-MIDI-client.\n" );

	m_midiClientName = midiDummy::name();

	return new midiDummy;
}









mixer::fifoWriter::fifoWriter( mixer * _mixer, fifo * _fifo ) :
	m_mixer( _mixer ),
	m_fifo( _fifo ),
	m_writing( true )
{
}




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




void mixer::fifoWriter::run( void )
{
#if 0
#ifdef LMMS_BUILD_LINUX
#ifdef LMMS_HAVE_PTHREAD_H
	cpu_set_t mask;
	CPU_ZERO( &mask );
	CPU_SET( 0, &mask );
	pthread_setaffinity_np( pthread_self(), sizeof( mask ), &mask );
#endif
#endif
#endif

	const fpp_t frames = m_mixer->framesPerPeriod();
	while( m_writing )
	{
		sampleFrameA * buffer = alignedAllocFrames( frames );
		const sampleFrameA * b = m_mixer->renderNextBuffer();
		alignedMemCpy( buffer, b, frames * sizeof( sampleFrameA ) );
		m_fifo->write( buffer );
	}

	m_fifo->write( NULL );
}




#include "moc_mixer.cxx"

