/*************************************************************************************************/
/*!
   	@file		AudioDevicePair.h
	@author 	Fanzo
*/
/*************************************************************************************************/
#pragma		once

///////////////////////////////////////////////////////////////////////////////////////////////////
//include files
#include	"iFace/iAudioDevicePair.h"

#pragma pack( push , 8 )		//set align

namespace icubic_audio
{
using namespace icubic;
///////////////////////////////////////////////////////////////////////////////////////////////////
// preprocessor deifne

///////////////////////////////////////////////////////////////////////////////////////////////////
// type define

///////////////////////////////////////////////////////////////////////////////////////////////////
// classes define

/**************************************************************************************************
"InputSyncQue" class 
**************************************************************************************************/
class InputSyncQue
{
	class Hdr
	{
	public:
		float**		m_ptr;
		bool		m_active;
		Hdr() : m_ptr( 0 ) , m_active( false ){}
	};
// variable member
private:
	uint32				m_output_que_samples;
	uint32				m_input_que_samples;
	uint32				m_channel;
	uint32				m_framesamples;
	
	// controls	
	CriticalSection		m_cs;
	bool				m_popf;
	Hdr*				m_pop_hdr;
	
	// frame buffer
	Array<Hdr>			m_frame_ptrs;
	Array<float*>		m_stream_ptrs;
	Array<float>		m_buffer;
	
	// que
	uint32				m_output_count;
	uint32				m_que_count;
	Array<Hdr*>			m_que;

// private functions
//=================================================================================================
void PushQue
		(
		Hdr*		hdr
		)
{
	cb_assert( m_que_count < m_input_que_samples + m_output_que_samples , L"algorithm error." );
	m_que[ m_que_count ]	= hdr;
	m_que_count++;
}
//=================================================================================================
Hdr* PopQue()
{
	cb_assert( m_que_count != 0 , L"algorithm error." );
	Hdr*	hdr = m_que[0];
	uint32	off , num = m_que_count;
	for( off = 0 ; off < num - 1 ; off++ )
		m_que[off]	= m_que[off+1];
	m_que_count--;
	return hdr;
}
//=================================================================================================
void PopQue
		(
		uint32	quenum
		)
{
	cb_assert( m_que_count >= quenum , L"algorithm error." );

	uint32	off , move = m_que_count - quenum;
	for( off = 0 ; off < move ; off++ )
		m_que[off]->m_active = false;
	for( off = 0 ; off < quenum ; off++ )
		m_que[off]	= m_que[off+move];
	m_que_count	= quenum;
}
// public functions
public:
//=================================================================================================
InputSyncQue() : 
		m_framesamples( 0 ) , 
		m_input_que_samples( 2 ) , 
		m_output_que_samples( 2 ) , 
		m_output_count( 2 ) , 
		m_channel( 0 ) , 
		m_que_count( 0 ) , 
		m_popf( false ) , 
		m_pop_hdr( 0 )
{
}
//=================================================================================================
void Initialize
		(
		uint32	output_samples , 
		uint32	input_samples , 
		uint32	channel, 
		uint32	framesamples
		)
{
	cb_assert( output_samples >= 1 && input_samples >= 1 , L"invalid args" );

	m_framesamples			= framesamples;
	m_output_que_samples	= output_samples;
	m_input_que_samples		= input_samples;
	m_channel				= channel;
	m_popf					= false;
	m_pop_hdr				= 0;
	
	// frame buffer
	uint32	que_samples = output_samples + input_samples;
	m_buffer.Resize( channel * framesamples * ( que_samples + 2 + 1 ) );
	MemoryZero( m_buffer.GetPtr() , m_buffer.GetDatanum() * sizeof( float ) );
	
	m_stream_ptrs.Resize( channel * (que_samples + 2 + 1 ) );
	uint32	off;
	for( off = 0 ; off < channel * (que_samples + 2 + 1 ) ; off++ )
		m_stream_ptrs[off]	= &m_buffer[ off * framesamples ];

	m_frame_ptrs.Resize( que_samples + 2 + 1 );
	for( off = 0 ; off < que_samples + 2 + 1 ; off++ )
	{
		m_frame_ptrs[off].m_ptr		= &m_stream_ptrs[off * channel];
		m_frame_ptrs[off].m_active	= false;
	}
	
	// que
	m_output_count	= m_output_que_samples;
	m_que_count		= 0;
	m_que.Resize( m_output_que_samples + m_input_que_samples );
}
//=================================================================================================
void Reset()
{
	m_popf			= false;
	m_output_count	= m_output_que_samples;
	m_que_count		= 0;
	m_pop_hdr		= 0;
}
//=================================================================================================
float const*const* PopBegin()
{
	cb_assert( m_pop_hdr == 0 , L"PopBegin called prev PopEnd" );
	
	Autolock	lock( m_cs );
	m_popf	= true;
	if( m_output_count > 0 )
	{
		m_output_count--;
		return m_frame_ptrs[0].m_ptr;
	}
	if( m_que_count == 0 )
	{
		cb_trace( L"!!!!Pop sync overflow\n" );
		m_output_count	= m_output_que_samples;
		return m_frame_ptrs[0].m_ptr;
	}
	m_pop_hdr	= PopQue();
	cb_assert( m_pop_hdr->m_active == true , L"algorithm error." );
	return m_pop_hdr->m_ptr;
}
//=================================================================================================
void PopEnd()
{
//	cb_assert( m_pop_hdr != 0 , L"PopEnd called prev PopBegin" );

	Autolock	lock( m_cs );

	if( m_pop_hdr != 0 )
	{	
		cb_assert( m_pop_hdr->m_active == true , L"algorithm error." );
		m_pop_hdr->m_active = false;
	}
	m_pop_hdr = 0;
}
//=================================================================================================
void Push
		(
		float const* const*	frame
		)
{
	Hdr*	hdr = 0;
	{
		Autolock	lock( m_cs );
		if( m_popf == false )
			return;
		uint32	off , num = m_frame_ptrs.GetDatanum();
		for( off = 1 ; off < num ; off++ )
		{
			if( m_frame_ptrs[off].m_active == false )
			{
				hdr	= &m_frame_ptrs[off];
				break;
			}
		}
		cb_assert( hdr != 0 , L"alogrithm error" );
	}
	
	// copy
	uint32	choff;
	for( choff = 0 ; choff < m_channel ; choff++ )
		MemoryCopy( hdr->m_ptr[choff] , frame[choff] , m_framesamples * sizeof( float ) );
	{
		Autolock	lock( m_cs );
		if( m_que_count == m_que.GetDatanum() )
		{
			cb_trace( L"!!!!!Push sync overflow\n" );
			PopQue( m_output_que_samples );
			return;
		}
		hdr->m_active	= true;
		PushQue( hdr );
	}
}
};
/**************************************************************************************************
"AudioDevicePair" class 
**************************************************************************************************/
class AudioDevicePair : 
	virtual public object_base , 
	public IAudioDevice , 
	public IAudioDeviceStream
{
	query_begin();
	iface_hook( IAudioDevice , IAudioDevice_IID )
	query_end( object_base );
	
// variable member
private:
	State						m_state;
	int32						m_ctrlid;
	IAudioDeviceStream*			m_stream_ptr;

	bool						m_active[2];
	uint32						m_max_channel[2];
	uint32						m_min_buffersamples;
	uint32						m_max_buffersamples;
	uint32						m_prefer_buffersamples;
	iAudioDevice				m_device[2];

	// thread
	IAudioDeviceStream::Format	m_format;
	InputSyncQue				m_syncque;
	
	// const
	static const uint32			m_output_delay	= 2;
	static const uint32			m_input_delay	= 2;

// thread functions
private:
//=================================================================================================
void AudioDeviceStreamSyncInput
		(
		const IAudioDeviceStream::Format&	format , 
		float const*const*					capture , 
		float**								render
		)
{
	cb_trace( L"Pair Input\n" );
	m_syncque.Push( capture );
}
//=================================================================================================
void AudioDeviceStreamSyncOutput
		(
		const IAudioDeviceStream::Format&	format , 
		float const*const*					capture , 
		float**								render
		)
{
	cb_trace( L"Pair Output\n" );
	if( m_stream_ptr == 0 )
		return;
	m_stream_ptr->AudioDeviceStream( m_ctrlid , m_format , m_syncque.PopBegin() , render );
	m_syncque.PopEnd();
}
//=================================================================================================
void cb_call AudioDeviceStream
		(
		int32								ctrlid , 
		const IAudioDeviceStream::Format&	format , 
		float const*const*					capture , 
		float**								render
		)
{
	if( m_active[0] == false || m_active[0] == false )
	{
		if( m_stream_ptr != 0 )	
			m_stream_ptr->AudioDeviceStream( m_ctrlid , format , capture , render );
		return;
	}
	if( ctrlid == 0 )
		AudioDeviceStreamSyncInput( format , capture , render );
	else if( ctrlid == 1 )
		AudioDeviceStreamSyncOutput( format , capture , render );
}
// private functions
private:
//=================================================================================================
IAudioDevice::Format GetInputFmt
		(
		const IAudioDevice::Format&	fmt
		)
{
	IAudioDevice::Format	in_fmt = fmt;
	in_fmt.m_channel[1]	= 0;
	return in_fmt;
}
//=================================================================================================
IAudioDevice::Format GetOutputFmt
		(
		const IAudioDevice::Format&	fmt
		)
{
	IAudioDevice::Format	out_fmt = fmt;
	out_fmt.m_channel[0]	= 0;
	return out_fmt;
}
// "IAudioDevice" interface functions
public:
//=================================================================================================
bool cb_call SetAudioDeviceStream
		(
		int32				ctrlid , 
		IAudioDeviceStream*	stream
		)
{
	if( m_state == Play_State )
		return false;
	m_ctrlid		= ctrlid;
	m_stream_ptr	= stream;
	return true;
}
//=================================================================================================
wstring cb_call GetDeviceName()
{
	return m_device[0]->GetDeviceName() + L"/" + m_device[1]->GetDeviceName();
}
//=================================================================================================
void cb_call GetChannelMaximum
		(
		uint32*		in_ch , 
		uint32*		out_ch
		)
{
	store( in_ch , m_max_channel[0] );
	store( out_ch , m_max_channel[1] );
}
//=================================================================================================
bool cb_call GetBufferSamplesMaximum
		(
		uint32*		min		= 0 , 
		uint32*		max		= 0 , 
		uint32*		prefer	= 0
		)
{
	store( min , m_min_buffersamples );
	store( max , m_max_buffersamples );
	store( prefer , m_prefer_buffersamples );
	return true;
}
//=================================================================================================
bool cb_call IsFormatAvailable
		(
		const IAudioDevice::Format&	fmt
		)
{
	if( fmt.m_channel[0] != 0 && false == m_device[0]->IsFormatAvailable( GetInputFmt( fmt ) ) )
		return false;	
	if( fmt.m_channel[1] != 0 && false == m_device[1]->IsFormatAvailable( GetOutputFmt( fmt ) ) )
		return false;
	return true;
}
//=================================================================================================
bool cb_call GetLatency
		(
		uint32*		input_ms , 
		uint32*		output_ms
		)
{
	if( m_state == Null_State )
		return false;
	uint32	in	= 0 , out = 0;
	if( m_device[0] == true )
		m_device[0]->GetLatency( &in , 0 );
	if( m_device[1] == true )
		m_device[1]->GetLatency( 0 , &out );
	store( input_ms , in );
	store( output_ms , out );
	return true;
}
//=================================================================================================
State cb_call GetState()
{
	return m_state;
}
//=================================================================================================
bool cb_call Create
		(
		const IAudioDevice::Format&	sfmt , 
		IAudioDeviceStream::Format*	tfmt	= 0
		)
{
	Destroy();
	if( sfmt.m_channel[0] == 0 && sfmt.m_channel[1] == 0 )
		return false;

	IAudioDevice::Format		fmt = sfmt;
	fmt.m_buffersamples	= ( fmt.m_buffersamples == 0 ) ? m_prefer_buffersamples : fmt.m_buffersamples;
	
	IAudioDevice::Format		sfmts[2] = { GetInputFmt( fmt ) , GetOutputFmt( fmt ) };
	IAudioDeviceStream::Format	tfmts[2];

	m_active[0]	= sfmts[0].m_channel[0] != 0 ? true : false;
	m_active[1]	= sfmts[1].m_channel[1] != 0 ? true : false;

	if( m_active[0] == true && false == m_device[0]->Create( sfmts[0] , &tfmts[0] ) )
		return false;
	if( m_active[1] == true && false == m_device[1]->Create( sfmts[1] , &tfmts[1] ) )
		return false;

	if( m_active[0]==true && m_active[1] == true )
	{
		if( tfmts[0].m_buffersamples != tfmts[1].m_buffersamples
		||  tfmts[0].m_samplespersec != tfmts[1].m_samplespersec )
		{
			if( m_active[0] == true )	m_device[0]->Destroy();
			if( m_active[1] == true )	m_device[1]->Destroy();
			return false;
		}
	}
	if( m_active[0] == true && m_active[1] == true )
		m_syncque.Initialize( m_output_delay , m_input_delay , tfmts[0].m_channel[0] , tfmts[0].m_buffersamples );

	m_format.m_buffersamples	= tfmts[0].m_buffersamples;
	m_format.m_channel[0]		= tfmts[0].m_channel[0];
	m_format.m_channel[1]		= tfmts[1].m_channel[1];
	m_format.m_samplespersec	= tfmts[0].m_samplespersec;
	store( tfmt , m_format );

	m_state	= Stop_State;
	return true;
}
//=================================================================================================
bool cb_call Play()
{
	if( m_state == Play_State )
		return true;
	if( m_state != Stop_State )
		return false;
	
	if( m_active[0] == true && m_active[1] == true )
		m_syncque.Reset();

	if( m_active[0] == true && false == m_device[0]->Play() )
		return false;
	if( m_active[1] == true && false == m_device[1]->Play() )
	{
		if( m_active[0] == true )	m_device[0]->Stop();
		return false;
	}		
	m_state	= Play_State;
	return true;
}
//=================================================================================================
void cb_call Stop()
{
	if( m_state != Play_State )
		return;
	if( m_active[0] == true )	m_device[0]->Stop();
	if( m_active[1] == true )	m_device[1]->Stop();
	m_state	= Stop_State;
}
//=================================================================================================
void cb_call Destroy()
{
	if( m_state == Null_State )
		return;
	if( m_active[0] == true )	m_device[0]->Destroy();
	if( m_active[1] == true )	m_device[1]->Destroy();
	m_active[0]	= false;
	m_active[1]	= false;
	m_state	= Null_State;
}

// public functions
public:
//=================================================================================================
AudioDevicePair() : 
		m_state( Null_State ) , 
		m_ctrlid( 0 ) , 
		m_stream_ptr( 0 ) , 
		m_min_buffersamples( 0 ) , 
		m_max_buffersamples( 0 ) , 
		m_prefer_buffersamples( 0 )
{
	m_max_channel[0]	= 0; 
	m_max_channel[1]	= 0; 
	m_active[0]			= false;
	m_active[1]			= false;
}
//=================================================================================================
~AudioDevicePair()
{
	Destroy();
}
//=================================================================================================
bool Initialize
		(
		iAudioDevice&	in_device , 
		iAudioDevice&	out_device
		)
{
	if( in_device == false || out_device == false )
		return false;
	in_device->Destroy();
	out_device->Destroy();
	
	uint32	in_min = 0 , in_max = 0 , out_min = 0 , out_max = 0;
	if( false == in_device->GetBufferSamplesMaximum( &in_min , &in_max ) )
		return false;
	if( false == out_device->GetBufferSamplesMaximum( &out_min , &out_max , &m_prefer_buffersamples ) )
		return false;
	uint32	min	= max( in_min , out_min );
	uint32	max	= min( in_max , out_max );
	if( max < min )
		return false;
	
	m_min_buffersamples	= min;
	m_max_buffersamples	= max;
	m_device[0]		= in_device;
	m_device[1]		= out_device;
	m_device[0]->GetChannelMaximum( &m_max_channel[0] , 0 );
	m_device[1]->GetChannelMaximum( 0 , &m_max_channel[1] );
	cb_verify( true == m_device[0]->SetAudioDeviceStream( 0 , this ) );
	cb_verify( true == m_device[1]->SetAudioDeviceStream( 1 , this ) );
	return true;
}
};

/**************************************************************************************************
"tEnumAudioDevicePair" class 
**************************************************************************************************/
template<class t_enum_class>
class tEnumAudioDevicePair : 
	virtual public object_base , 
	public IEnumAudioDevicePair
{
	query_begin();
	iface_hook( IEnumAudioDevice , IEnumAudioDevice_IID )
	iface_hook( IEnumAudioDevicePair , IEnumAudioDevicePair_IID )
	query_end( object_base );
	
// variable member
private:
	instance<t_enum_class>	m_enum;
	Array<uint32>			m_inputs;
	Array<uint32>			m_outputs;

// private functions
private:
//=================================================================================================
iAudioDevice CreateAudioDevice
		(
		int32		in_devid , 
		int32		out_devid , 
		wndptr		owner
		)
{
	if( in_devid < 0 && out_devid < 0 )
		return iAudioDevice();
	if( in_devid < 0 )
		return m_enum->CreateDevice( m_outputs[out_devid] , owner );
	if( out_devid < 0 )
		return m_enum->CreateDevice( m_inputs[in_devid] , owner	);

	iAudioDevice	in	= m_enum->CreateDevice
			( 
			m_inputs[in_devid] , 
			owner
			);
	iAudioDevice	out	= m_enum->CreateDevice
			( 
			m_outputs[out_devid] , 
			owner		
			);
	if( in == false || out == false )
		return iAudioDevice();
	
	instance<AudioDevicePair>	dev;
	if( false == dev->Initialize( in , out ) )
		return iAudioDevice();
	return (iAudioDevice)dev;
}
// "IEnumAudioDevice" interface functions
public:
//=================================================================================================
void cb_call Enum()
{
	m_enum->Enum();
	m_inputs.Resize( 0 );
	m_outputs.Resize( 0 );

	uint32		off , num = m_enum->GetDeviceNum();
	for( off = 0 ; off < num ; off++ )
	{
		IEnumAudioDevice::DeviceInfo	info;
		m_enum->GetDeviceInfo( off , &info );
		if( info.m_type == IEnumAudioDevice::Input )
			m_inputs[ m_inputs.Add() ]		= off;
		else if( info.m_type == IEnumAudioDevice::Output )
			m_outputs[ m_outputs.Add() ]	= off;
	}
}
//=================================================================================================
bool cb_call GetAudioDeviceId
		(
		int32			devoff , 
		IAudioDeviceId*	id
		)
{
	int32	in , out;
	if( false == DeviceToPair( devoff , &in , &out ) )
		return false;

	instance<MemStreamWrite>	mem;
	mem->Open();

	if( false == Save( (iStreamWrite)mem , GetEnumId() ) )
		return false;

	bool	f[2]	= { in < 0 ? false : true , out < 0 ? false : true };
	if( false == mem->Write( f , sizeof( f[0] ) , _countof( f ) , Little_EndianType ) )
		return false;

	if( f[0] == true )
	{
		instance<AudioDeviceId>		t_id;
		if( false == m_enum->GetAudioDeviceId( m_inputs[in] , (iAudioDeviceId)t_id ) )
			return false;
		int32		size = t_id->GetDataSize();
		if( false == mem->Write( &size , sizeof( size ) , 1 , Little_EndianType ) )
			return false;
		if( false == mem->Write( t_id->GetDataPtr() , 1 , size , Little_EndianType ) )
			return false;
	}
	if( f[1] == true )
	{
		instance<AudioDeviceId>		t_id;
		if( false == m_enum->GetAudioDeviceId( m_outputs[out] , (iAudioDeviceId)t_id ) )
			return false;
		int32		size = t_id->GetDataSize();
		if( false == mem->Write( &size , sizeof( size ) , 1 , Little_EndianType ) )
			return false;
		if( false == mem->Write( t_id->GetDataPtr() , 1 , t_id->GetDataSize() , Little_EndianType ) )
			return false;
	}
	id->SetData( mem->GetDataPtr() , mem->GetDataSize() );
	return true;
}
//=================================================================================================
bool cb_call SearchDevice
		(
		int32*			devoff , 
		IAudioDeviceId*	id
		)
{
	instance<MemStreamRead>	mem;
	mem->Open( id->GetDataPtr() , id->GetDataSize() );

	guid	enumid;	
	if( false == Load( (iStreamRead)mem , &enumid ) )
		return false;
	if( enumid != GetEnumId() )
		return false;
		
	bool	f[2];
	if( false == mem->Read( f , sizeof( f[0] ) , _countof( f ) , Little_EndianType ) )
		return false;

	int32	in = -1 , out = -1;
	if( f[0] == true )
	{
		instance<AudioDeviceId>		t_id;
		{
			int		size;
			if( false == mem->Read( &size , sizeof( size ) , 1 , Little_EndianType ) )
				return false;
			Array<uint8>	buf( size );
			if( false == mem->Read( buf.GetPtr() , 1 , size , Little_EndianType ) )
				return false;
			t_id->SetData( buf.GetConstPtr() , buf.GetDatanum() );
		}		
		if( false == m_enum->SearchDevice( &in , (iAudioDeviceId)t_id ) )
			return false;
		in	= m_inputs.Search( in );
		if( in < 0 )
			return false;
	}
	if( f[1] == true )
	{
		instance<AudioDeviceId>		t_id;
		{
			int		size;
			if( false == mem->Read( &size , sizeof( size ) , 1 , Little_EndianType ) )
				return false;
			Array<uint8>	buf( size );
			if( false == mem->Read( buf.GetPtr() , 1 , size , Little_EndianType ) )
				return false;
			t_id->SetData( buf.GetConstPtr() , buf.GetDatanum() );
		}		
		if( false == m_enum->SearchDevice( &out , (iAudioDeviceId)t_id ) )
			return false;
		out	= m_outputs.Search( out );
		if( out < 0 )
			return false;
	}
	int32	t_devoff;
	if( false == PairToDevice( &t_devoff , in , out ) )
		return false;
	store( devoff , t_devoff );
	return true;
}
//=================================================================================================
int32 cb_call GetDeviceNum()
{
	int32	num;
	cb_verify( true == PairToDevice( &num , m_inputs.GetDatanum() - 1 , m_outputs.GetDatanum() - 1 ) );
	num++;
	return num;
}
//=================================================================================================
bool cb_call GetDeviceInfo
		(
		int32		devoff , 
		DeviceInfo*	info
		)
{
	int32	in , out;
	if( false == DeviceToPair( devoff , &in , &out ) )
		return false;
	if( out >= 0 )
		return GetOutputDeviceInfo( out , info );
	if( in >= 0 )
		return GetInputDeviceInfo( in , info );
	return false;
}
//=================================================================================================
iAudioDevice cb_call CreateDevice
		(
		int32		devoff
#ifdef cb_windows
		, HWND		owner
#endif		
		)
{
	int32	in , out;
	if( false == DeviceToPair( devoff , &in , &out ) )
		return iAudioDevice();
	return CreateAudioDevice
			( 
			in , 
			out 
#ifdef cb_windows
			, owner
#endif		
			);
}
// "IEnumAudioDevicePair" interface functions
public:
//=================================================================================================
bool cb_call PairToDevice
		(
		int32*	devoff , 
		int32	in_devoff , 
		int32	out_devoff	
		)
{
	if( in_devoff >= m_inputs.GetDatanum() || out_devoff >= m_outputs.GetDatanum() )
		return false;
	in_devoff	= in_devoff < 0 ? -1 : in_devoff;
	out_devoff	= out_devoff < 0 ? -1 : out_devoff;
	store( devoff , ( ( in_devoff + 1 ) + ( out_devoff + 1 ) * ( m_inputs.GetDatanum() + 1 ) ) - 1 );
	return true;
}
//=================================================================================================
bool cb_call DeviceToPair
		(
		int32	devoff , 
		int32*	in_devoff , 
		int32*	out_devoff
		)
{
	if( devoff < 0 )
		return false;
	int32	in	= ( devoff + 1 ) % ( m_inputs.GetDatanum() + 1 ) - 1;
	int32	out	= ( devoff + 1 ) / ( m_inputs.GetDatanum() + 1 ) - 1;
	if( in == -1 && out == -1 )
		return false;
	if( in >= m_inputs.GetDatanum() || out >= m_inputs.GetDatanum() )
		return false;
	store( in_devoff , in );
	store( out_devoff , out );
	return true;
}
//=================================================================================================
int32 cb_call GetInputDeviceNum()
{
	return m_inputs.GetDatanum();
}
//=================================================================================================
bool cb_call GetInputDeviceInfo
		(
		int32							devid , 
		IEnumAudioDevice::DeviceInfo*	info
		)
{
	if( devid < 0 || devid >= m_inputs.GetDatanum() )
		return false;
	return m_enum->GetDeviceInfo( m_inputs[ devid ] , info );
}
//=================================================================================================
int32 cb_call GetOutputDeviceNum()
{
	return m_outputs.GetDatanum();
}
//=================================================================================================
bool cb_call GetOutputDeviceInfo
		(
		int32							devid , 
		IEnumAudioDevice::DeviceInfo*	info
		)
{
	if( devid < 0 || devid >= m_outputs.GetDatanum() )
		return false;
	return m_enum->GetDeviceInfo( m_outputs[ devid ] , info );
}
// protected functions
protected:
//=================================================================================================
virtual
guid GetEnumId() = 0;

// public functions
public:
//=================================================================================================
tEnumAudioDevicePair()
{
}
};


namespace mme
{
/**************************************************************************************************
"EnumAudioDevicePair" class 
**************************************************************************************************/
class EnumAudioDevicePair : public tEnumAudioDevicePair<EnumAudioDevice>
{
// "IEnumAudioDevice" interface functions
public:
//=================================================================================================
wstring cb_call GetDriverName()
{
	return wstring( L"Multi Media Extension" );
}
// protected functions
protected:
//=================================================================================================
guid GetEnumId()
{
	cb_guid_define( IdentifierId , 0xFC4D9594 , 0x7F634f60 , 0x8DBF9C33 , 0x641AE17B );
	static
	IdentifierId	id;
	return id;	
}
};
};

namespace ds
{
/**************************************************************************************************
"EnumAudioDevicePair" class 
**************************************************************************************************/
class EnumAudioDevicePair : public tEnumAudioDevicePair<EnumAudioDevice>
{
// "IEnumAudioDevice" interface functions
public:
//=================================================================================================
wstring cb_call GetDriverName()
{
	return wstring( L"Direct Sound" );
}
// protected functions
protected:
//=================================================================================================
guid GetEnumId()
{
	cb_guid_define( IdentifierId , 0x6F05586F , 0xDB6E4924 , 0xA07BF156 , 0x922171C0 );
	static
	IdentifierId	id;
	return id;	
}
};
};

namespace wdm
{
/**************************************************************************************************
"EnumAudioDevicePair" class 
**************************************************************************************************/
class EnumAudioDevicePair : public tEnumAudioDevicePair<EnumAudioDevice>
{
// "IEnumAudioDevicePair" interface functions
public:
//=================================================================================================
wstring cb_call GetDriverName()
{
	return wstring( L"WDM Kernel Streaming" );
}
// protected functions
protected:
//=================================================================================================
guid GetEnumId()
{
	cb_guid_define( IdentifierId , 0x4E202C04 , 0x803F4c21 , 0xAAD4FBF8 , 0xFEEA8742 );
	static
	IdentifierId	id;
	return id;	
}
};
};

///////////////////////////////////////////////////////////////////////////////////////////////////
// global variable define

///////////////////////////////////////////////////////////////////////////////////////////////////
// global functions define

};	//namespace

//using namespace icubic;		

#pragma pack( pop )			//release align
