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

///////////////////////////////////////////////////////////////////////////////////////////////////
//include files
#include	"iFace/iAudioDevice.h"
#include	<windows.h>
#include	<mmreg.h>
#include	<ks.h>
#include	<ksmedia.h>
#include	<setupapi.h>
#include	<mmsystem.h>

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

namespace icubic_audio
{
namespace wdm
{
using namespace icubic;

///////////////////////////////////////////////////////////////////////////////////////////////////
// preprocessor deifne

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

///////////////////////////////////////////////////////////////////////////////////////////////////
// shared class
/**************************************************************************************************
"MultiBuffer" class 
**************************************************************************************************/
template<class t_class>
class MultiBuffer
{
	cb_copy_impossible( MultiBuffer );
// variable member
private:
	Array<uint8>	m_buffer;
	t_class*		m_ptr;
	
// public functions
public:
//=================================================================================================
MultiBuffer() : m_ptr( 0 )
{
}
//=================================================================================================
void Resize
		(
		uint32		size
		)
{
	m_buffer.Resize( size );
	m_ptr	= (t_class*)m_buffer.GetPtr();
}
//=================================================================================================
t_class* Ptr()
{
	return m_ptr;
}
};
typedef		MultiBuffer<KSMULTIPLE_ITEM>	KsMultipleItem;

/**************************************************************************************************
"KsIdentifierAry" class 
**************************************************************************************************/
class KsIdentifierAry : public Array<KSIDENTIFIER>
{
// public functions
public:
//=================================================================================================
void SetKSMULTIPLE_ITEM
		(
		const KSMULTIPLE_ITEM&	items
		)
{
	cb_assert( items.Count * sizeof( KSIDENTIFIER ) == items.Size - sizeof( KSMULTIPLE_ITEM )  , L"invalid items size." );

	KSIDENTIFIER*	p = ( KSIDENTIFIER* )( &items + 1 );
	int		off , num = items.Count;
	Resize( num );
	for( off = 0 ; off < num ; off++ )
	{
		( *this )[ off ]	= p[ off ];
	}
}
};
/**************************************************************************************************
"KsDataRange" class 
**************************************************************************************************/
class KsDataRange : public Array<KSDATARANGE*>
{
	cb_copy_impossible( KsDataRange );
// variable members
	unsigned char*		m_pdata;
	
// private functions
private:
//=================================================================================================
void Reset()
{
	if( m_pdata != 0 )
		delete	[]m_pdata;
	m_pdata	= 0;
	Resize( 0 );
}
// public functions
public:
//=================================================================================================
KsDataRange() : m_pdata( 0 )
{
}
//=================================================================================================
~KsDataRange()
{
	Reset();
}
//=================================================================================================
void SetKSMULTIPLE_ITEM
		(
		const KSMULTIPLE_ITEM&	items
		)
{
	Reset();
	if( items.Size == 0 || items.Count == 0 )
		return;
	m_pdata	= new unsigned char[ items.Size ];
	MemoryCopy( m_pdata , ( &items + 1 ) , items.Size - sizeof( KSMULTIPLE_ITEM ) );
	
	int		size = 0 , off , num = items.Count;
	Resize( num );
	KSDATARANGE*	p = (KSDATARANGE*)m_pdata;
	for( off = 0 ; off < num ; off++ )
	{
		(*this)[off]	= p;
		size += p->FormatSize;
		p	= (KSDATARANGE*)( ((char*)p) + p->FormatSize );
	}
	cb_assert( size == items.Size - sizeof( KSMULTIPLE_ITEM ) , L"invalid KsDataRange size." );
}
};

///////////////////////////////////////////////////////////////////////////////////////////////////
// shared functions
//=================================================================================================
cb_inline
bool DeviceIoControl_sync
		(
		HANDLE		handle , 
		DWORD		code , 
		VOID*		in , 
		ULONG		in_size , 
		VOID*		out , 
		ULONG		out_size , 
		ULONG*		byte_returnd = 0
        )
{
	if ( false == IsValidHandle( handle ) )
		return false;

	OVERLAPPED		overlapped;
	MemoryZero( &overlapped , sizeof( overlapped ) );
	overlapped.hEvent	= CreateEvent( NULL , FALSE , FALSE , NULL );
	if( overlapped.hEvent == NULL )
		return false;
	overlapped.hEvent = ( HANDLE )( ( DWORD_PTR )overlapped.hEvent | 0x1);

	ULONG	t_byte_returnd	= 0;
	if( FALSE == DeviceIoControl
			(
			handle , 
			code , 
			in , 
			in_size , 
			out , 
			out_size , 
			&t_byte_returnd , 
			&overlapped 
			) )
	{
		DWORD	lasterr = GetLastError();
		if ( lasterr == ERROR_IO_PENDING && ( WAIT_OBJECT_0 != ::WaitForSingleObject( overlapped.hEvent , INFINITE ) ) )
		{
	        SafeCloseHandle( overlapped.hEvent );
			return false;
		}
        else if ( t_byte_returnd == 0 )
		{
	        SafeCloseHandle( overlapped.hEvent );
			return false;
        }
	}
	store( byte_returnd , t_byte_returnd );
	SafeCloseHandle( overlapped.hEvent );
    return true;
}
//=================================================================================================
cb_inline
bool Set_KSPROPERTY
		(
		HANDLE		handle , 
		REFGUID		propset ,
		ULONG		propid ,
		PVOID		value ,
		ULONG		size
		)
{
	KSPROPERTY		ksprop;
	ksprop.Set    = propset; 
	ksprop.Id     = propid;
	ksprop.Flags  = KSPROPERTY_TYPE_SET;
    ULONG		returned = 0;
    if( false == DeviceIoControl_sync
			(
            handle,
            IOCTL_KS_PROPERTY ,
            &ksprop ,
            sizeof( ksprop ) ,
            value ,
            size ,
            &returned
            ) )
			return false;
    return true;
}
//=================================================================================================
cb_inline
bool Get_KSPROPERTY
		(
		HANDLE		handle , 
		REFGUID		propset ,
		ULONG		propid ,
		PVOID		value ,
		ULONG		size
		)
{
	KSPROPERTY		ksprop;
	ksprop.Set    = propset; 
	ksprop.Id     = propid;
	ksprop.Flags  = KSPROPERTY_TYPE_GET;
    ULONG		returned = 0;
    if( false == DeviceIoControl_sync
			(
            handle,
            IOCTL_KS_PROPERTY ,
            &ksprop ,
            sizeof( ksprop ) ,
            value ,
            size ,
            &returned
            ) )
			return false;
    return true;
}
//=================================================================================================
cb_inline
bool Get_KSP_PIN
		(
		HANDLE	handle , 
		ULONG   pinid , 
		REFGUID propset , 
		ULONG   propid , 
		PVOID   value , 
		ULONG   size
		)
{
    KSP_PIN		prop;
    prop.Property.Set	= propset;
    prop.Property.Id	= propid;
    prop.Property.Flags	= KSPROPERTY_TYPE_GET;
    prop.PinId			= pinid;
    prop.Reserved		= 0;
    if( false == DeviceIoControl_sync
			(
			handle , 
			IOCTL_KS_PROPERTY,
			&prop ,
			sizeof( KSP_PIN ),
			value ,
			size
			) )
			return false;
    return true;
}
//=================================================================================================
cb_inline
bool GetSize_KSP_PIN
		(
		HANDLE	handle , 
		ULONG   pinid , 
		REFGUID propset , 
		ULONG   propid , 
		ULONG*	size
		)
{
    KSP_PIN		prop;
    prop.Property.Set	= propset;
    prop.Property.Id	= propid;
    prop.Property.Flags	= KSPROPERTY_TYPE_GET;
    prop.PinId			= pinid;
    prop.Reserved		= 0;
    if( false == DeviceIoControl_sync
			(
			handle , 
			IOCTL_KS_PROPERTY,
			&prop ,
			sizeof( KSP_PIN ),
			0 ,
			0 , 
			size
			) )
			return false;
    return true;
}
//=================================================================================================
cb_inline
bool Get_KSMULTIPLE_ITEM
		(
		HANDLE				handle , 
		ULONG				pinid , 
		REFGUID				propset ,
		ULONG				propid ,
		KsMultipleItem&		items
		)
{
	ULONG	size;
	if( false == GetSize_KSP_PIN( handle , pinid , propset , propid , &size ) )
		return false;
	items.Resize( size );
	if( false == Get_KSP_PIN( handle , pinid , propset , propid , items.Ptr() , size ) )
		return false;
    return true;
}
//=================================================================================================
cb_inline
bool Get_KSIDENTIFIER
		(
		HANDLE				handle , 
		ULONG				pinid , 
		REFGUID				propset ,
		ULONG				propid ,
		KsIdentifierAry&	ary
		)
{
	KsMultipleItem	multiitem;
	if( false == Get_KSMULTIPLE_ITEM
			(
			handle , 
			pinid , 
			propset ,
			propid ,
			multiitem
			) )
			return false;
	ary.SetKSMULTIPLE_ITEM( *multiitem.Ptr() );
	return true;
}
//=================================================================================================
cb_inline
bool Get_KSDATARANGE
		(
		HANDLE			handle , 
		ULONG			pinid , 
		REFGUID			propset ,
		ULONG			propid ,
		KsDataRange&	items
		)
{
	KsMultipleItem	multiitem;
	if( false == Get_KSMULTIPLE_ITEM
			(
			handle , 
			pinid , 
			propset ,
			propid ,
			multiitem
			) )
			return false;
	items.SetKSMULTIPLE_ITEM( *multiitem.Ptr() );
	return true;
}
//=================================================================================================
cb_inline
ULONG Get_KSPROPERTY_PIN_CTYPES
		(
		HANDLE	handle
		)
{
	ULONG	num = 0;
	Get_KSP_PIN( handle , 0 , KSPROPSETID_Pin , KSPROPERTY_PIN_CTYPES , &num , sizeof( num ) );
	return num;
}
//=================================================================================================
cb_inline
KSPIN_COMMUNICATION Get_KSPROPERTY_PIN_COMMUNICATION
		(
		HANDLE	handle , 
		ULONG	pinid
		)
{
	KSPIN_COMMUNICATION	comm	= KSPIN_COMMUNICATION_NONE;
	Get_KSP_PIN( handle , pinid , KSPROPSETID_Pin , KSPROPERTY_PIN_COMMUNICATION , &comm , sizeof( comm ) );
    return comm;
}
//=================================================================================================
cb_inline
bool Get_KSPROPERTY_PIN_DATAFLOW
		(
		HANDLE			handle , 
		ULONG			pinid , 
		KSPIN_DATAFLOW*	rflow
		)
{
	KSPIN_DATAFLOW	flow;
	if( false == Get_KSP_PIN( handle , pinid , KSPROPSETID_Pin , KSPROPERTY_PIN_DATAFLOW , &flow , sizeof( flow ) ) )
		return false;
	store( rflow , flow );
    return true;
}
//=================================================================================================
cb_inline
bool Get_KSPROPERTY_PIN_CINSTANCES
		(
		HANDLE				handle , 
		ULONG				pinid , 
		KSPIN_CINSTANCES*	rcins
		)
{
	KSPIN_CINSTANCES	cins;
	if( false == Get_KSP_PIN( handle , pinid , KSPROPSETID_Pin , KSPROPERTY_PIN_CINSTANCES , &cins , sizeof(cins) ) )
		return false;
	store( rcins , cins );
    return true;
}
//=================================================================================================
cb_inline
bool Get_KSPROPERTY_PIN_GLOBALCINSTANCES
		(
		HANDLE				handle , 
		ULONG				pinid , 
		KSPIN_CINSTANCES*	rcins
		)
{
	KSPIN_CINSTANCES	cins;
	if( false == Get_KSP_PIN( handle , pinid , KSPROPSETID_Pin , KSPROPERTY_PIN_GLOBALCINSTANCES , &cins , sizeof(cins) ) )
		return false;
	store( rcins , cins );
    return true;
}
//=================================================================================================
cb_inline
bool Get_KSPROPERTY_PIN_NECESSARYINSTANCES
		(
		HANDLE				handle , 
		ULONG				pinid , 
		KSPIN_CINSTANCES*	rins
		)
{
	KSPIN_CINSTANCES	ins;
	if( false == Get_KSP_PIN( handle , pinid , KSPROPSETID_Pin , KSPROPERTY_PIN_NECESSARYINSTANCES , &ins , sizeof(ins) ) )
		return false;
	store( rins , ins );
    return true;
}
//=================================================================================================
cb_inline
bool Get_KSPROPERTY_PIN_CATEGORY
		(
		HANDLE		handle , 
		ULONG		pinid , 
		GUID*		category
		)
{
	GUID	v;
	if( false == Get_KSP_PIN( handle , pinid , KSPROPSETID_Pin , KSPROPERTY_PIN_CATEGORY , &v , sizeof(v) ) )
		return false;
	store( category , v );
    return true;
}
//=================================================================================================
cb_inline
bool Get_KSPROPERTY_PIN_NAME
		(
		HANDLE		handle , 
		ULONG		pinid , 
		wstring*	name
		)
{
	wchar_t	v[ MAX_PATH ];
	v[0]	= L'\0';
	if( false == Get_KSP_PIN( handle , pinid , KSPROPSETID_Pin , KSPROPERTY_PIN_NAME , v , sizeof(v) - 1 ) )
		return false;
	*name	= v;
    return true;
}
//=================================================================================================
cb_inline
bool Set_KSSTATE
		(
		HANDLE		handle , 
		KSSTATE		ksstate
		)
{
	if( false == Set_KSPROPERTY
			(
			handle , 
			KSPROPSETID_Connection,
			KSPROPERTY_CONNECTION_STATE,
			&ksstate,
			sizeof( ksstate )
			) )
			return false;
	return true;
}
//=================================================================================================
cb_inline
bool Get_KSSTATE
		(
		HANDLE		handle , 
		KSSTATE*	ksstate
		)
{
	if( false == Get_KSPROPERTY
			(
			handle , 
			KSPROPSETID_Connection,
			KSPROPERTY_CONNECTION_STATE,
			ksstate,
			sizeof( ksstate )
			) )
			return false;
	return true;
}
//=================================================================================================
cb_inline
HANDLE KsCreatePin
		(
		HANDLE			driver , 
		PKSPIN_CONNECT	connect , 
		ACCESS_MASK		access
		)
{
	typedef	KSDDKAPI DWORD WINAPI KsCreatePinFunc
		(
		HANDLE , 
		PKSPIN_CONNECT , 
		ACCESS_MASK , 
		PHANDLE
		) ;
	static
    KsCreatePinFunc*    KsCreatePin	= NULL;
    if( KsCreatePin == NULL )
    {
		HMODULE	library = ::LoadLibraryW( L"ksuser.dll" );
		if( library == NULL )
			return NULL;
		KsCreatePin = ( KsCreatePinFunc* )::GetProcAddress( library , "KsCreatePin" );
		if ( NULL == KsCreatePin )
			return NULL;
    }
    HANDLE	pin = NULL;
    if( ERROR_SUCCESS != KsCreatePin
			(
			driver , 
			connect , 
			access , 
			&pin
			) )
			return false;
	return pin;
}
//=================================================================================================
cb_inline
IAudioDeviceStream::Format FmtToFmt
		(
		const IAudioDevice::Format&	fmt
		)
{
	IAudioDeviceStream::Format	sfmt;
	sfmt.m_channel[0]		= fmt.m_channel[0];
	sfmt.m_channel[1]		= fmt.m_channel[1];
	sfmt.m_samplespersec	= fmt.m_samplespersec;
	sfmt.m_buffersamples	= fmt.m_buffersamples;
	
	return sfmt;
}

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

/**************************************************************************************************
"PinStream" class 
**************************************************************************************************/
class PinStream
{
// variable member
private:
	unsigned char				m_connect_buffer[ sizeof( KSPIN_CONNECT ) + sizeof( KSDATAFORMAT_WAVEFORMATEX ) - sizeof( WAVEFORMATEX ) + sizeof( WAVEFORMATEXTENSIBLE ) ];
	KSPIN_CONNECT*				m_connect;
	KSDATAFORMAT_WAVEFORMATEX*	m_ksdata;
	WAVEFORMATEXTENSIBLE*		m_format;
	HANDLE						m_handle;
	
// public functions
public:
//=================================================================================================
PinStream() : 
		m_handle( NULL ) , 
		m_connect( (KSPIN_CONNECT*)&m_connect_buffer[0] ) , 
		m_ksdata( (KSDATAFORMAT_WAVEFORMATEX*)( m_connect_buffer + sizeof( KSPIN_CONNECT ) ) ) , 
		m_format( (WAVEFORMATEXTENSIBLE*)( m_connect_buffer + sizeof( KSPIN_CONNECT ) + sizeof( KSDATAFORMAT_WAVEFORMATEX ) - sizeof( WAVEFORMATEX ) ) )
{
	m_connect->Interface.Set				= KSINTERFACESETID_Standard;
	m_connect->Interface.Id					= KSINTERFACE_STANDARD_STREAMING;
	m_connect->Interface.Flags				= 0;
	m_connect->Medium.Set					= KSMEDIUMSETID_Standard;
	m_connect->Medium.Id					= KSMEDIUM_TYPE_ANYINSTANCE;
	m_connect->Medium.Flags					= 0;
	m_connect->PinId						= 0;
	m_connect->PinToHandle					= NULL;
	m_connect->Priority.PriorityClass		= KSPRIORITY_NORMAL;
	m_connect->Priority.PrioritySubClass	= 1;
	m_ksdata->DataFormat.FormatSize			= sizeof( KSDATAFORMAT_WAVEFORMATEX ) - sizeof( WAVEFORMATEX ) + sizeof( WAVEFORMATEXTENSIBLE );
	m_ksdata->DataFormat.Flags				= 0;
	m_ksdata->DataFormat.SampleSize			= 0;
	m_ksdata->DataFormat.Reserved			= 0;
	m_ksdata->DataFormat.MajorFormat		= KSDATAFORMAT_TYPE_AUDIO;
	m_ksdata->DataFormat.SubFormat			= KSDATAFORMAT_SUBTYPE_PCM;
	m_ksdata->DataFormat.Specifier			= KSDATAFORMAT_SPECIFIER_WAVEFORMATEX;
    m_format->Format.wFormatTag				= WAVE_FORMAT_EXTENSIBLE;//WAVE_FORMAT_PCM;
    m_format->Format.cbSize					= sizeof( WAVEFORMATEXTENSIBLE ) - sizeof( WAVEFORMATEX );
}
//=================================================================================================
~PinStream()
{
	Destroy();
}
//=================================================================================================
bool Create
		(
		HANDLE			handle , 
		ULONG			pinid , 
		uint32			channel , 
		const GUID&		sampletype , 
		uint32			bitspersample , 
		uint32			samplefreq
		)
{
	Destroy();
	m_connect->PinId						= pinid;
    m_ksdata->DataFormat.SubFormat			= sampletype;//sampletype == IAudioDevice::Integer ? KSDATAFORMAT_SUBTYPE_PCM : KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
	m_ksdata->DataFormat.SampleSize			= channel * bitspersample / 8;
    m_format->Format.nChannels				= (WORD)channel;
    m_format->Format.wBitsPerSample			= (WORD)bitspersample;
    m_format->Format.nSamplesPerSec			= samplefreq;
    m_format->Format.nBlockAlign			= m_format->Format.nChannels * m_format->Format.wBitsPerSample / 8;
    m_format->Format.nAvgBytesPerSec		= m_format->Format.nSamplesPerSec * m_format->Format.nBlockAlign;
    m_format->Samples.wValidBitsPerSample	= m_format->Format.wBitsPerSample;
    m_format->SubFormat						= m_ksdata->DataFormat.SubFormat;
    m_format->dwChannelMask					= ( 1 << channel ) - 1;

	m_handle	= KsCreatePin
			(
			handle , 
			m_connect , 
			GENERIC_WRITE | GENERIC_READ
			);
	if( m_handle == NULL )
		return false;
	return true;
}
//=================================================================================================
void Destroy()
{
	if( m_handle != NULL )
		CloseHandle( m_handle );
	m_handle	= NULL;
}
//=================================================================================================
bool IsCreated()
{
	return m_handle == NULL ? false : true;
}
//=================================================================================================
WAVEFORMATEX GetFormat()
{
	return m_format->Format;
}
//=================================================================================================
bool Run()
{
	KSSTATE	state;
	if( false == Get_KSSTATE( m_handle , &state ) )
		return false;
	if( state == KSSTATE_STOP )
	{
		if( false == Set_KSSTATE( m_handle , KSSTATE_ACQUIRE ) )
			return false;
		if( false == Set_KSSTATE( m_handle , KSSTATE_PAUSE ) )
			return false;
		if( false == Set_KSSTATE( m_handle , KSSTATE_RUN ) )
			return false;
		return true;
	}
	else if( state == KSSTATE_ACQUIRE )
	{
		if( false == Set_KSSTATE( m_handle , KSSTATE_PAUSE ) )
			return false;
		if( false == Set_KSSTATE( m_handle , KSSTATE_RUN ) )
			return false;
		return true;
	}
	else if( state == KSSTATE_PAUSE  )
	{
		if( false == Set_KSSTATE( m_handle , KSSTATE_RUN ) )
			return false;
		return true;
	}
	else if( state == KSSTATE_RUN  )
	{
		return true;
	}
	return false;
}
//=================================================================================================
bool Pause()
{
	KSSTATE	state;
	if( false == Get_KSSTATE( m_handle , &state ) )
		return false;
	if( state == KSSTATE_STOP )
	{
		if( false == Set_KSSTATE( m_handle , KSSTATE_ACQUIRE ) )
			return false;
		if( false == Set_KSSTATE( m_handle , KSSTATE_PAUSE ) )
			return false;
		return true;
	}
	else if( state == KSSTATE_ACQUIRE )
	{
		if( false == Set_KSSTATE( m_handle , KSSTATE_PAUSE ) )
			return false;
		return true;
	}
	else if( state == KSSTATE_PAUSE  )
	{
		return true;
	}
	else if( state == KSSTATE_RUN  )
	{
		if( false == Set_KSSTATE( m_handle , KSSTATE_PAUSE ) )
			return false;
		return true;
	}
	return false;
}
//=================================================================================================
bool Stop()
{
	KSSTATE	state;
	if( false == Get_KSSTATE( m_handle , &state ) )
		return false;
	if( state == KSSTATE_STOP )
	{
		return true;
	}
	else if( state == KSSTATE_ACQUIRE )
	{
		if( false == Set_KSSTATE( m_handle , KSSTATE_STOP ) )
			return false;
		return true;
	}
	else if( state == KSSTATE_PAUSE  )
	{
		if( false == Set_KSSTATE( m_handle , KSSTATE_STOP ) )
			return false;
		return true;
	}
	else if( state == KSSTATE_RUN  )
	{
		if( false == Set_KSSTATE( m_handle , KSSTATE_PAUSE ) )
			return false;
		if( false == Set_KSSTATE( m_handle , KSSTATE_STOP ) )
			return false;
		return true;
	}
	return false;
}
//=================================================================================================
bool WriteData
		(
		KSSTREAM_HEADER*	header,
		OVERLAPPED*			overlapped
		)
{
	header->DataUsed	= header->FrameExtent;
	DWORD	returned	= 0;
	if( FALSE == DeviceIoControl
			(
			m_handle ,
			IOCTL_KS_WRITE_STREAM ,
			NULL ,
			0 ,
			header ,
			header->Size ,
			&returned ,
			overlapped
			) )
	{
		DWORD	err = GetLastError();
		if ( ERROR_IO_PENDING != err )
			return false;
		cb_trace( L"ERROR_IO_PENDING\n" );
	}
    return true;
}
//=================================================================================================
bool ReadData
		(
		KSSTREAM_HEADER*	header,
		OVERLAPPED*			overlapped
		)
{
	header->DataUsed	= 0;
	DWORD	returned	= 0;
	if( FALSE == DeviceIoControl
			(
			m_handle ,
			IOCTL_KS_READ_STREAM ,
			NULL ,
			0 ,
			header ,
			header->Size ,
			&returned ,
			overlapped
			) )
	{
		DWORD	err = GetLastError();
		if ( ERROR_IO_PENDING != err )
			return false;
		cb_trace( L"ERROR_IO_PENDING\n" );
	}
	else
	{
		cb_trace( L"ReadData true\n" );
	}
    return true;
}
};
/**************************************************************************************************
"AudioDevice" class 
**************************************************************************************************/
class AudioDevice : 
	virtual public object_base , 
	public Thread , 
	public IAudioDevice
{
	query_begin();
	iface_hook( IAudioDevice , IAudioDevice_IID )
	query_end( object_base );

	class Packet
	{
		cb_copy_impossible( Packet );
		
	public:
		KSSTREAM_HEADER m_header;
		OVERLAPPED		m_overlapped;
		Array<uint8>	m_buffer;
		
		Packet()
		{
			MemoryZero( &m_header , sizeof( m_header ) );
			MemoryZero( &m_overlapped , sizeof( m_overlapped ) );
			m_overlapped.hEvent	= CreateEvent( NULL , FALSE , FALSE , NULL );
		}
		void Initialize
				(
				uint32				channel , 
				BufferSampleFormat	samplefmt , 
				uint32				samplenum
				)
		{
			int		byte	= SampleFormat_to_Byte( samplefmt );
			int		size	= channel * byte * samplenum;
		
			m_buffer.Resize( size );
			{
				uint8*	p = m_buffer.GetPtr();
				uint32	sampleoff;
				for( sampleoff = 0 ; sampleoff < samplenum * channel ; sampleoff++ )
				{
					float_to_BufferSampleFormat( 0.0f , samplefmt , p );
					p += byte;
				}
			}
			m_header.Data							= m_buffer.GetPtr();
			m_header.FrameExtent					= size;
			m_header.DataUsed						= size;
			m_header.Size							= sizeof( m_header );
			m_header.PresentationTime.Numerator		= 1;
			m_header.PresentationTime.Denominator	= 1;
		}
	};
	enum CmdType
	{
		Play_cmd , 
		Stop_cmd
	};
	class SampleType
	{
	public:
		GUID					m_type;
		uint32					m_samplebit;
		BufferSampleFormat		m_format;
	};

// variable member
private:
	// infos
	wstring						m_driver_path;
	wstring						m_device_name;
	ULONG						m_pinid;
	uint32						m_maximum_input_channel;
	uint32						m_maximum_output_channel;
	Array<SampleType>			m_sampletypes;
	State						m_state;
	
	// thread event
	CmdType						m_cmd;
	HANDLE						m_event;
	HANDLE						m_event_r;

	// thread
	PinStream					m_pin_stream;
	IAudioDeviceStream::Format	m_format;
	BufferSampleFormat			m_buffer_format;
	
	// callback
	int32						m_ctrlid;
	IAudioDeviceStream*			m_stream_ptr;

	// const
	static	const uint32		m_buffersamples_min		= 1;
	static	const uint32		m_buffersamples_max		= 32 * 1024;
	static	const uint32		m_buffersamples_prefer	=  4 * 1024;
	
// thread fuctions	
private:
//=================================================================================================
int ThreadProc()
{
	::SetThreadPriority( GetCurrentThread() , 15 );

	if( m_format.m_channel[0] != 0  )
		ThreadCapture();
	else if( m_format.m_channel[1] != 0 )
		ThreadRender();
	return 0;
}
//=================================================================================================
bool ThreadCaptureEvent
		(
		Packet	packet[] , 
		HANDLE	events[] , 
		int		pknum
		)
{
	if( m_cmd == Play_cmd )
	{
		ResetEvent( m_event );
		SetEvent( m_event_r );
		cb_verify( true == m_pin_stream.Run() );
		int	pkoff;
		for( pkoff = 0 ; pkoff < pknum ; pkoff++ )
		{
			SetEvent( packet[ pkoff ].m_overlapped.hEvent );
			cb_verify( true == m_pin_stream.ReadData( &packet[ pkoff ].m_header , &packet[ pkoff ].m_overlapped ) );
		}
	}
	else if( m_cmd == Stop_cmd )
	{
		WaitForMultipleObjects( pknum , events , TRUE , INFINITE );
		cb_verify( true == m_pin_stream.Stop() );
		ResetEvent( m_event );
		SetEvent( m_event_r );
		return false;
	}
	return true;
}
//=================================================================================================
void ThreadCapture()
{
	// initialize header
    Packet	packet[ 2 ];
    {
		int		off;
		for( off = 0 ; off < _countof( packet ) ; off++ )
			packet[ off ].Initialize( m_format.m_channel[0] , m_buffer_format , m_format.m_buffersamples );
	}
    // initialize events
	HANDLE      events[ _countof( packet ) + 1 ];
	events[ 0 ]	= m_event;
	{
		int		off;
		for( off = 0 ; off < _countof( packet ) ; off++ )
			events[ off + 1 ]	= packet[ off ].m_overlapped.hEvent;
	}	
	// stream buffer
	Array<float>	stream_buffer( m_format.m_channel[0] * m_format.m_buffersamples );
	Array<float*>	buffer_ptr( m_format.m_channel[0] );
	{
		uint32	ch;
		for( ch = 0 ; ch < m_format.m_channel[0] ; ch++ )
			buffer_ptr[ch] = &stream_buffer[ ch * m_format.m_buffersamples ];
	}
	float**	buffer = buffer_ptr.GetPtr();
	
	// stream
	while( true )
	{
		int	evoff	= WaitForMultipleObjects( _countof( events ) , events , FALSE , INFINITE ) - WAIT_OBJECT_0;
		int	pkoff	= evoff - 1;
		if( 0 <= pkoff && pkoff < _countof( packet ) )
		{
			if( m_stream_ptr != 0 )
			{
				Interleave_to_float( m_format.m_channel[0] , m_format.m_buffersamples , m_buffer_format , packet[ pkoff ].m_buffer.GetPtr() , buffer );
				m_stream_ptr->AudioDeviceStream( m_ctrlid , m_format , (const float**)buffer , 0 );
			}
			SetEvent( packet[ pkoff ].m_overlapped.hEvent );
			cb_verify( true == m_pin_stream.ReadData( &packet[ pkoff ].m_header , &packet[ pkoff ].m_overlapped ) );
			cb_trace( L"%d : ReadData\n" , pkoff );
		}
		else if( evoff == 0 )
		{
			if( false == ThreadCaptureEvent( packet , &events[1] , _countof( packet ) ) )
				break;
		}
	}
}
//=================================================================================================
bool ThreadRenderEvent
		(
		Packet	packet[] ,
		HANDLE	events[] , 
		int		pknum
		)
{
	if( m_cmd == Play_cmd )
	{
		ResetEvent( m_event );
		SetEvent( m_event_r );
		cb_verify( true == m_pin_stream.Run() );
		int	pkoff;
		for( pkoff = 0 ; pkoff < pknum ; pkoff++ )
			SetEvent( packet[pkoff].m_overlapped.hEvent );
	}
	else if( m_cmd == Stop_cmd )
	{
		WaitForMultipleObjects( pknum , events , TRUE , INFINITE );
		cb_verify( true == m_pin_stream.Stop() );
		ResetEvent( m_event );
		SetEvent( m_event_r );
		return false;
	}
	return true;
}
//=================================================================================================
void ThreadRender()
{
	// initialize header
    Packet	packet[ 2 ];
    {
		int		off;
		for( off = 0 ; off < _countof( packet ) ; off++ )
			packet[ off ].Initialize( m_format.m_channel[1] , m_buffer_format , m_format.m_buffersamples );
	}
    // initialize events
	HANDLE      events[ _countof( packet ) + 1 ];
	events[ 0 ]	= m_event;
	{
		int		off;
		for( off = 0 ; off < _countof( packet ) ; off++ )
			events[ off + 1 ]	= packet[ off ].m_overlapped.hEvent;
	}
	// stream buffer
	Array<float>	stream_buffer( m_format.m_channel[1] * m_format.m_buffersamples );
	Array<float*>	buffer_ptr( m_format.m_channel[1] );
	MemoryZero( stream_buffer.GetPtr() , stream_buffer.GetDatanum() * sizeof( float ) );
	{
		uint32	ch;
		for( ch = 0 ; ch < m_format.m_channel[1] ; ch++ )
			buffer_ptr[ch] = &stream_buffer[ ch * m_format.m_buffersamples ];
	}
	float**	buffer = buffer_ptr.GetPtr();
	
	// stream
	while( true )
	{
		int	evoff	= WaitForMultipleObjects( _countof( events ) , events , FALSE , INFINITE ) - WAIT_OBJECT_0;
		int	pkoff	= evoff - 1;
		if( 0 <= pkoff && pkoff < _countof( packet ) )
		{
			if( m_stream_ptr != 0 )
			{
				m_stream_ptr->AudioDeviceStream( m_ctrlid , m_format , 0 , buffer );
				float_to_Interleave( m_format.m_channel[1] , m_format.m_buffersamples , (const float**)buffer , m_buffer_format , packet[ pkoff ].m_buffer.GetPtr() );
			}
			SetEvent( packet[ pkoff ].m_overlapped.hEvent );
			cb_verify( true == m_pin_stream.WriteData( &packet[ pkoff ].m_header , &packet[ pkoff ].m_overlapped ) );
			cb_trace( L"%d : WriteData\n" , pkoff );
		}
		else if( evoff == 0 )
		{
			if( false == ThreadRenderEvent( packet , &events[1] , _countof( packet ) ) )
				break;
		}
	}
}
// private functions
private:
//=================================================================================================
void Cmd
		(
		CmdType		cmd
		)
{
	m_cmd	= cmd;
	ResetEvent( m_event_r );
	SetEvent( m_event );
	WaitForSingleObject( m_event_r , INFINITE );
}
// "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_name;
}
//=================================================================================================
void cb_call GetChannelMaximum
		(
		uint32*		in_ch , 
		uint32*		out_ch
		)
{
	store( in_ch , m_maximum_input_channel );
	store( out_ch , m_maximum_output_channel );
}
//=================================================================================================
bool cb_call GetBufferSamplesMaximum
		(
		uint32*		min , 
		uint32*		max , 
		uint32*		prefer
		)
{
	store( min , m_buffersamples_min );
	store( max , m_buffersamples_max );
	store( prefer , m_buffersamples_prefer );
	return true;
}
//=================================================================================================
bool cb_call IsFormatAvailable
		(
		const Format&	fmt
		)
{
	Format	t_fmt	= fmt;
	if( t_fmt.m_buffersamples == 0 )
		t_fmt.m_buffersamples = m_buffersamples_prefer;
	instance<AudioDevice>	device;
	if( false == device->Initialize
			( 
			m_driver_path.c_str() , 
			m_device_name.c_str() , 
			m_pinid , 
			m_maximum_input_channel , 
			m_maximum_output_channel 
			) )
			return false;
	if( false == device->Create( t_fmt , 0 ) )
		return false;
	return true;
}
//=================================================================================================
bool cb_call Create
		(
		const Format&				fmt , 
		IAudioDeviceStream::Format*	sfmt
		)
{
	Destroy();
	
	Format	t_fmt = fmt;
	if( t_fmt.m_buffersamples == 0 )
		t_fmt.m_buffersamples	= m_buffersamples_prefer;
	
	if( t_fmt.m_channel[0] == 0 && t_fmt.m_channel[1] == 0 )
		return false;
	uint32		channel;
	if( t_fmt.m_channel[0] != 0 )
		channel = t_fmt.m_channel[0];
	else
		channel = t_fmt.m_channel[1];

	HANDLE	handle = CreateFileW
			(
			m_driver_path.c_str() , 
			GENERIC_READ | GENERIC_WRITE,
			0,
			NULL,
			OPEN_EXISTING,
			FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
			NULL
			);
    if ( false == IsValidHandle( handle ) )
		return false;
	
	int		typeoff , typenum = m_sampletypes.GetDatanum();
	for( typeoff = 0 ; typeoff < typenum ; typeoff++ )
	{
		if( true == m_pin_stream.Create
			( 
			handle , 
			m_pinid , 
			channel , 
			m_sampletypes[typeoff].m_type , 
			m_sampletypes[typeoff].m_samplebit , 
			t_fmt.m_samplespersec 
			) )
			break;
	}
	CloseHandle( handle );
	if( typeoff == typenum )
		return false;
	
	m_format		= FmtToFmt( t_fmt );
	m_buffer_format	= m_sampletypes[typeoff].m_format;
	m_state			= Stop_State;
	store( sfmt , m_format );
	return true;
}
//=================================================================================================
void cb_call Destroy()
{
	if( m_state == Null_State )
		return;
	Stop();
	m_pin_stream.Destroy();
	m_state	= Null_State;
}
//=================================================================================================
bool cb_call GetLatency
		(
		uint32*		input_ms , 
		uint32*		output_ms
		)
{
	if( m_state == Null_State )
		return false;
	long	in	= m_format.m_channel[0] == 0 ? 0 : m_format.m_buffersamples * 1000 / m_format.m_samplespersec;
	long	out	= m_format.m_channel[1] == 0 ? 0 : m_format.m_buffersamples * 1000 / m_format.m_samplespersec;
	store( input_ms , (uint32)in );
	store( output_ms , (uint32)out );
	return true;
}
//=================================================================================================
State cb_call GetState()
{
	return m_state;
}
//=================================================================================================
bool cb_call Play()
{
	if( m_state == Play_State )
		return true;
	if( m_state != Stop_State )
		return false;
	
	if( false == Thread::Create() )
		return false;
	m_state	= Play_State;
	Cmd( Play_cmd );
	return true;
}
//=================================================================================================
void cb_call Stop()
{
	if( m_state != Play_State )
		return;
	
	m_state	= Stop_State;
	Cmd( Stop_cmd );
	Thread::Destroy();
}
// public functions
public:
//=================================================================================================
AudioDevice() : 
		m_ctrlid( 0 ) , 
		m_stream_ptr( 0 ) , 
		m_pinid( -1 ) , 
		m_state( Null_State ) , 
		m_maximum_input_channel( 0 ) , 
		m_maximum_output_channel( 0 ) , 
		m_buffer_format( Int8Align8 )
{
	m_event		= CreateEvent( NULL , FALSE , FALSE , NULL );
	m_event_r	= CreateEvent( NULL , FALSE , FALSE , NULL );
	
	m_sampletypes.Resize( 5 );
	m_sampletypes[0].m_type			= KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
	m_sampletypes[0].m_samplebit	= 32;
	m_sampletypes[0].m_format		= Float32Align32LSB;
	m_sampletypes[1].m_type			= KSDATAFORMAT_SUBTYPE_PCM;
	m_sampletypes[1].m_samplebit	= 32;
	m_sampletypes[1].m_format		= Int32Align32LSB;
	m_sampletypes[2].m_type			= KSDATAFORMAT_SUBTYPE_PCM;
	m_sampletypes[2].m_samplebit	= 24;
	m_sampletypes[2].m_format		= Int24Align24LSB;
	m_sampletypes[3].m_type			= KSDATAFORMAT_SUBTYPE_PCM;
	m_sampletypes[3].m_samplebit	= 16;
	m_sampletypes[3].m_format		= Int16Align16LSB;
	m_sampletypes[4].m_type			= KSDATAFORMAT_SUBTYPE_PCM;
	m_sampletypes[4].m_samplebit	= 8;
	m_sampletypes[4].m_format		= Int8Align8;
}
//=================================================================================================
~AudioDevice()
{
	Destroy();
	SafeCloseHandle( m_event );
	SafeCloseHandle( m_event_r );
}
//=================================================================================================
bool Initialize
		(
		const wchar_t*					driver_path , 
		const wchar_t*					device_name , 
		ULONG							pinid , 
		uint32							max_input_ch , 
		uint32							max_output_ch
		)
{
	m_driver_path				= driver_path;
	m_device_name				= device_name;
	m_pinid						= pinid;
	m_maximum_input_channel		= max_input_ch;
	m_maximum_output_channel	= max_output_ch;
	return true;
}
};
/**************************************************************************************************
"PinInfo" class 
**************************************************************************************************/
class PinInfo
{
	class PinDescriptor
	{
	public:
		wstring					m_name;
		GUID					m_category;
		KsIdentifierAry			m_interfaces;
		KsIdentifierAry			m_mediums;
		KsDataRange				m_dataranges;
		KsDataRange				m_constrdataranges;
		KSPIN_DATAFLOW          m_dataflow;
		KSPIN_COMMUNICATION     m_communication;
		KSPIN_CINSTANCES        m_cinstances;
		KSPIN_CINSTANCES        m_cinstances_global;
		KSPIN_CINSTANCES        m_cinstances_necessary;
	}; 
// variable member
private:
	ULONG						m_pinid;
	PinDescriptor				m_desc;
	Array<KSDATARANGE_AUDIO>	m_format;
	wstring						m_driver_path;
	wstring						m_device_name;

// private functions
private:
//=================================================================================================
bool UpdateFormat()
{
	int		doff , dnum = m_desc.m_dataranges.GetDatanum();
	for( doff = 0 ; doff < dnum ; doff++ )
	{
		KSDATARANGE*	data = m_desc.m_dataranges[doff];
		if( IsEqualGUID( data->MajorFormat , KSDATAFORMAT_TYPE_AUDIO )
		&&( IsEqualGUID( data->SubFormat , KSDATAFORMAT_SUBTYPE_PCM ) || IsEqualGUID( data->SubFormat , KSDATAFORMAT_SUBTYPE_IEEE_FLOAT ) ) )
		{
			KSDATARANGE_AUDIO*	data_audio = (KSDATARANGE_AUDIO*)data;
			m_format[ m_format.Add() ]	= *data_audio;
#ifdef cb_debug
			if( IsEqualGUID( data->SubFormat , KSDATAFORMAT_SUBTYPE_PCM ) )
				cb_trace( L"KSDATAFORMAT_SUBTYPE_PCM\n" )
			else
				cb_trace( L"KSDATAFORMAT_SUBTYPE_IEEE_FLOAT\n" )
			cb_trace( L"samplesize=%d\n"				, data->SampleSize );
			cb_trace( L"MaximumChannels=%d\n"			, data_audio->MaximumChannels );
			cb_trace( L"MinimumBitsPerSample=%d\n"		, data_audio->MinimumBitsPerSample );
			cb_trace( L"MaximumBitsPerSample=%d\n"		, data_audio->MaximumBitsPerSample );
			cb_trace( L"MinimumSampleFrequency=%d\n"	, data_audio->MinimumSampleFrequency );
			cb_trace( L"MaximumSampleFrequency=%d\n"	, data_audio->MaximumSampleFrequency );
#endif
		}
	}
	if( m_format.GetDatanum() == 0 )
		return false;
	return true;
}
//=================================================================================================
void GetChannelMaximum
		(
		uint32*		in_ch , 
		uint32*		out_ch
		)
{
	uint32	max_input_ch	= 0;
	uint32	max_output_ch	= 0;

	int		off , num = m_format.GetDatanum();
	for( off = 0 ; off < num ; off++ )
	{
		KSDATARANGE_AUDIO*	fmt = &m_format[off];
		if( m_desc.m_dataflow == KSPIN_DATAFLOW_OUT )
			max_input_ch	= max( max_input_ch , fmt->MaximumChannels );
		else
			max_output_ch	= max( max_output_ch , fmt->MaximumChannels );
	}
	store( in_ch , max_input_ch );
	store( out_ch , max_output_ch );
}
// public functions
public:
//=================================================================================================
PinInfo() : m_pinid( 0 )
{
}
//=================================================================================================
bool PinInfo::Enum
		(
		HANDLE			handle , 
		ULONG			pinid , 
		const wstring&	driver_path , 
		const wstring&	device_name
		)
{
	m_pinid			= pinid;
	m_driver_path	= driver_path;
	m_device_name	= device_name;
	
	m_desc.m_communication	= Get_KSPROPERTY_PIN_COMMUNICATION( handle , m_pinid );
#ifdef cb_debug	
if( m_desc.m_communication == KSPIN_COMMUNICATION_NONE )
	cb_trace( L"KSPIN_COMMUNICATION_NONE\n" )
else if( m_desc.m_communication == KSPIN_COMMUNICATION_SINK )
	cb_trace( L"KSPIN_COMMUNICATION_SINK\n" )
else if( m_desc.m_communication == KSPIN_COMMUNICATION_SOURCE )
	cb_trace( L"KSPIN_COMMUNICATION_SOURCE\n" )
else if( m_desc.m_communication == KSPIN_COMMUNICATION_BOTH )
	cb_trace( L"KSPIN_COMMUNICATION_BOTH\n" )
else if( m_desc.m_communication == KSPIN_COMMUNICATION_BRIDGE )
	cb_trace( L"KSPIN_COMMUNICATION_BRIDGE\n" )
#endif
	if( m_desc.m_communication != KSPIN_COMMUNICATION_SINK
	&&  m_desc.m_communication != KSPIN_COMMUNICATION_BOTH )
		return false;

	Get_KSIDENTIFIER
			(
			handle , 
			m_pinid , 
			KSPROPSETID_Pin,
			KSPROPERTY_PIN_INTERFACES ,
			m_desc.m_interfaces
			);
	Get_KSIDENTIFIER
			(
			handle , 
			m_pinid , 
			KSPROPSETID_Pin,
			KSPROPERTY_PIN_MEDIUMS ,
			m_desc.m_mediums
			);
	Get_KSDATARANGE
			(
			handle , 
			m_pinid , 
			KSPROPSETID_Pin,
			KSPROPERTY_PIN_DATARANGES ,
			m_desc.m_dataranges
			);			
	Get_KSDATARANGE
			(
			handle , 
			m_pinid , 
			KSPROPSETID_Pin,
			KSPROPERTY_PIN_CONSTRAINEDDATARANGES ,
			m_desc.m_constrdataranges
			);			
	if( false == Get_KSPROPERTY_PIN_DATAFLOW
			(
			handle , 
			m_pinid , 
			&m_desc.m_dataflow
			) )
			return false;
#ifdef cb_debug	
if( m_desc.m_dataflow == KSPIN_DATAFLOW_IN )
	cb_trace( L"[out]" )
else if( m_desc.m_dataflow == KSPIN_DATAFLOW_OUT )
	cb_trace( L"[in]" )
#endif	
	Get_KSPROPERTY_PIN_CINSTANCES
			(
			handle , 
			m_pinid , 
			&m_desc.m_cinstances
			);
	Get_KSPROPERTY_PIN_GLOBALCINSTANCES
			(
			handle , 
			m_pinid , 
			&m_desc.m_cinstances_global
			);
	Get_KSPROPERTY_PIN_NECESSARYINSTANCES
			(
			handle , 
			m_pinid , 
			&m_desc.m_cinstances_necessary
			);
	Get_KSPROPERTY_PIN_CATEGORY
			(
			handle , 
			m_pinid , 
			&m_desc.m_category
			);
	Get_KSPROPERTY_PIN_NAME
			(
			handle , 
			m_pinid , 
			&m_desc.m_name 
			);
	cb_trace( L"[pin %d] %s\n" , m_pinid , m_desc.m_name.c_str() );
	
	return UpdateFormat();
}
//=================================================================================================
wstring GetDeviceName()
{
	return m_device_name;
}
//=================================================================================================
IEnumAudioDevice::DeviceType GetDeviceType()
{
	if( m_desc.m_dataflow == KSPIN_DATAFLOW_OUT )
		return IEnumAudioDevice::Input;
	else
		return IEnumAudioDevice::Output;
}
//=================================================================================================
ULONG GetPinId()
{
	return m_pinid;
}
//=================================================================================================
wstring GetDriverPath()
{
	return m_driver_path;
}
//=================================================================================================
iAudioDevice CreateDevice()
{
	uint32	in_ch , out_ch;
	GetChannelMaximum( &in_ch , &out_ch );
	instance<AudioDevice>	device;
	if( false == device->Initialize( m_driver_path.c_str() , m_device_name.c_str() , m_pinid , in_ch , out_ch ) )
		return iAudioDevice();
	return (iAudioDevice)device;
}
};
/**************************************************************************************************
"DriverInfo" class 
**************************************************************************************************/
class DriverInfo
{
// variable member
private:
	Straightdata<PinInfo>	m_pins;
	wstring					m_driver_path;
	wstring					m_driver_name;
	wstring					m_driver_desc;
	
// private functions
private:
//=================================================================================================
bool EnumratePins
		(
		HANDLE			handle
		)
{
	ULONG	pinid , pinnum = Get_KSPROPERTY_PIN_CTYPES( handle );
	for( pinid = 0 ; pinid < pinnum ; pinid++ )
	{
	    KSPIN_COMMUNICATION comm	= Get_KSPROPERTY_PIN_COMMUNICATION( handle , pinid );
		if( comm != KSPIN_COMMUNICATION_SOURCE
		&&  comm != KSPIN_COMMUNICATION_SINK
		&&  comm != KSPIN_COMMUNICATION_BOTH )
			continue;

		int		doff = m_pins.Add();
		if( false == m_pins[ doff ].Enum( handle , pinid , m_driver_path , m_driver_name ) )
			m_pins.Delete( doff );
	}
	if( m_pins.GetDatanum() == 0 )
		return false;
	return true;
}
// public functions
public:
//=================================================================================================
DriverInfo()
{
}
//=================================================================================================
bool Enum
		(
		const wchar_t*		path , 
		const wchar_t*		devname , 
		const wchar_t*		devdesc
		)
{
	m_pins.Resize( 0 );
	m_driver_path	= path;
	m_driver_name	= devname;
	m_driver_desc	= devdesc;
	HANDLE	handle = CreateFileW
			(
			path , 
			GENERIC_READ | GENERIC_WRITE,
			0,
			NULL,
			OPEN_EXISTING,
			FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
			NULL
			);
    if ( false == IsValidHandle( handle ) )
		return false;
	
	cb_trace( L"\n[ %s ]\n" , devname );
	if( false == EnumratePins( handle ) )
	{
		CloseHandle( handle );
		return false;
	}
	if( true == IsValidHandle( handle ) )
		CloseHandle( handle );
	return true;
}
//=================================================================================================
int GetPinInfoNum()
{
	return m_pins.GetDatanum();
}
//=================================================================================================
PinInfo& GetPinInfo
		(
		int		off
		)
{
	return m_pins[off];
}
};
/**************************************************************************************************
"EnumAudioDevice" class 
**************************************************************************************************/
class EnumAudioDevice : 
	virtual public object_base , 
	public IEnumAudioDevice
{
	query_begin();
	iface_hook( IEnumAudioDevice , IEnumAudioDevice_IID )
	query_end( object_base );

	class DeviceId
	{
	public:
		wstring	m_path;
		ULONG	m_pinid;
		bool Load
				(
				IStreamRead*	stream
				)
		{
			if( false == Load_utf16( stream , &m_path ) )
				return false;
			if( false == stream->Read( &m_pinid , sizeof( m_pinid ) , 1 , Little_EndianType ) )
				return false;
			return true;
		}
		bool Save
				(
				IStreamWrite*	stream
				)
		{
			if( false == Save_utf16( stream , m_path ) )
				return false;
			if( false == stream->Write( &m_pinid , sizeof( m_pinid ) , 1 , Little_EndianType ) )
				return false;
			return true;
		}
	};

// variable members
private:
	Straightdata<DriverInfo>	m_driverlist;
	Array<PinInfo*>				m_pins;
	
// private functions
private:
//=================================================================================================
wstring cb_call GetDeviceString
		(
		HDEVINFO			devinfo , 
		SP_DEVINFO_DATA&	devinfodata , 
		DWORD				prop
		)
{
	DWORD	regtype;
	DWORD	size = 0;
	::SetupDiGetDeviceRegistryPropertyW
				(
				devinfo,
				&devinfodata,
				prop,
				&regtype,
				NULL,
				0,
				&size
				);
	if( size == 0 )
		return wstring();				
	wchar_t*	name = new wchar_t[ size ];
	if( FALSE == ::SetupDiGetDeviceRegistryPropertyW
				(
				devinfo,
				&devinfodata,
				prop,
				&regtype,
				(PBYTE)name,
				size,
				&size
				) )
	{
		delete	[]name;
		return wstring();
	}
	wstring	str = name;
	delete	[]name;
	return str;
}
//=================================================================================================
wstring cb_call GetDeviceDesc
		(
		HDEVINFO			devinfo , 
		SP_DEVINFO_DATA&	devinfodata
		)
{
	return GetDeviceString( devinfo , devinfodata , SPDRP_DEVICEDESC );
}
//=================================================================================================
wstring cb_call GetFriendlyName
		(
		HDEVINFO					devinfo , 
		SP_DEVINFO_DATA&			devinfodata , 
		SP_DEVICE_INTERFACE_DATA&	if_data
		)
{
	wstring	name = GetDeviceString( devinfo , devinfodata , SPDRP_FRIENDLYNAME );
	if(name.empty() == false )
		return name;

	HKEY hkey	= SetupDiOpenDeviceInterfaceRegKey( devinfo , &if_data , 0 , KEY_QUERY_VALUE );
	if( hkey == INVALID_HANDLE_VALUE )
		return name;

	DWORD	size;	
    LONG	err = RegQueryValueExW( hkey , L"FriendlyName" , NULL , NULL , NULL , &size );
    if( err != ERROR_SUCCESS || size == 0 )
    {
		RegCloseKey( hkey );
		return name;
    }
    wchar_t*	pbuf	= new wchar_t[ size ];
	err = RegQueryValueExW( hkey , L"FriendlyName" , NULL , NULL , ( PBYTE )pbuf , &size );
    if( err != ERROR_SUCCESS || size == 0 )
    {
		RegCloseKey( hkey );
		delete	[]pbuf;
		return name;
    }
    name	= pbuf;
    delete	[]pbuf;
	RegCloseKey( hkey );
	return name;
}

// "IEnumAudioDevice" interface functions
public:
//=================================================================================================
void cb_call Enum()
{
	Reset();

	GUID		category[]		= { STATIC_KSCATEGORY_AUDIO };
	GUID		alias[]			= { STATIC_KSCATEGORY_CAPTURE , STATIC_KSCATEGORY_RENDER };
	HDEVINFO	hcategory	= SetupDiGetClassDevs
				(
				&category[ 0 ] , 
				NULL, 
				NULL, 
				DIGCF_PRESENT | DIGCF_DEVICEINTERFACE
				);
	if( false == IsValidHandle( hcategory ) )
		return;

	DWORD								detail_size	= sizeof( SP_DEVICE_INTERFACE_DETAIL_DATA ) + MAX_PATH * sizeof(wchar_t);
	SP_DEVICE_INTERFACE_DETAIL_DATA_W*	pdetail		= ( SP_DEVICE_INTERFACE_DETAIL_DATA_W* )new BYTE[ detail_size ];

	int	iface_off;
	for( iface_off = 0 ;  ; iface_off++ )
	{
		// get interface data
		SP_DEVICE_INTERFACE_DATA	iface_data;
		MemoryZero( &iface_data , sizeof( iface_data ) );
		iface_data.cbSize		= sizeof( iface_data );
		if( FALSE == SetupDiEnumDeviceInterfaces
					(
					hcategory , 
					NULL, 
					&category[ 0 ] , 
					iface_off ,
					&iface_data
					) )
					break;

		// get device info
		pdetail->cbSize	= sizeof( SP_DEVICE_INTERFACE_DETAIL_DATA_W );
		SP_DEVINFO_DATA		dev_info;
		MemoryZero( &dev_info , sizeof( dev_info ) );
		dev_info.cbSize		= sizeof( dev_info );
		if( FALSE == SetupDiGetDeviceInterfaceDetailW
					(
					hcategory , 
					&iface_data , 
					pdetail , 
					detail_size , 
					NULL, 
					&dev_info
					) )
					continue;

		// get device name
		wstring	devdesc		= GetDeviceDesc( hcategory , dev_info );
		wstring devname		= GetFriendlyName( hcategory , dev_info , iface_data );

		// alias
		int		alias_f = 0;
		int		alias_off , alias_num = _countof( alias );
		for( alias_off = 0 ; alias_off < alias_num ; alias_off++ )
		{
			SP_DEVICE_INTERFACE_DATA	iface_alias;
			MemoryZero( &iface_alias , sizeof( iface_alias ) );
			iface_alias.cbSize	= sizeof( iface_alias );
			if( FALSE == SetupDiGetDeviceInterfaceAlias
						(
						hcategory , 
						&iface_data , 
						&alias[ alias_off ] , 
						&iface_alias
						) )
						continue;
			if( iface_alias.Flags == 0 || ( iface_alias.Flags & SPINT_REMOVED ) != 0 )
				continue;
			alias_f |= ( 1 << alias_off );
		}
		if( alias_f == 0 )
			continue;
		
		int		doff = m_driverlist.Add();
		if( false == m_driverlist[doff].Enum( pdetail->DevicePath , devname.c_str() , devdesc.c_str() ) )
		{
			m_driverlist.Delete( doff );
			continue;
		}
	}	
	if( pdetail != 0 )
		delete[](BYTE*)pdetail;
	if( true == IsValidHandle( hcategory ) )
		SetupDiDestroyDeviceInfoList( hcategory );
	
	// update pins
	int		driveroff , drivernum = m_driverlist.GetDatanum();
	for( driveroff = 0 ; driveroff < drivernum ; driveroff++ )
	{
		int	pinoff , pinnum = m_driverlist[ driveroff ].GetPinInfoNum();
		for( pinoff = 0 ; pinoff < pinnum ; pinoff++ )
		{
			m_pins[ m_pins.Add() ]	= &m_driverlist[ driveroff ].GetPinInfo( pinoff );
		}
	}
}
//=================================================================================================
wstring cb_call GetDriverName()
{
	return wstring( L"WDM Kernel Streaming" );
}
//=================================================================================================
guid GetEnumId()
{
	cb_guid_define( GetEnumId , 0xEEB7315E , 0xA5224aec , 0xB30FA324 , 0x368CE807 );
	static
	GetEnumId	id;
	return id;	
}

// "IEnumAudioDeviceCreate" interface functions
public:
//=================================================================================================
bool cb_call GetAudioDeviceId
		(
		int32			devoff , 
		IAudioDeviceId*	id
		)
{
	if( devoff < 0 || devoff >= m_pins.GetDatanum() )
		return false;
	DeviceId	devid;
	devid.m_path	= m_pins[devoff]->GetDriverPath();
	devid.m_pinid	= m_pins[devoff]->GetPinId();

	instance<MemStreamWrite>	mem;
	mem->Open();
	{
		if( false == Save( (iStreamWrite)mem , GetEnumId() ) )
			return false;
		if( false == devid.Save( (iStreamWrite)mem ) )
			return false;
	}
	id->SetData( mem->GetDataPtr() , mem->GetDataSize() );
	return true;
}
//=================================================================================================
bool cb_call SearchDevice
		(
		int32*			devoff , 
		IAudioDeviceId*	id
		)
{
	// get data
	DeviceId	devid;
	{
		instance<MemStreamRead>	mem;
		mem->Open( id->GetDataPtr() , id->GetDataSize() );
		guid	enumid;
		if( false == Load( (iStreamRead)mem , &enumid ) )
			return false;
		if( enumid != GetEnumId() ) 
			return false;
		if( false == devid.Load( (iStreamRead)mem ) )
			return false;
	}
	// search
	int32	off , num = m_pins.GetDatanum();
	for( off = 0 ; off < num ; off++ )
	{
		if( m_pins[off]->GetDriverPath() == devid.m_path && m_pins[off]->GetPinId() == devid.m_pinid )
		{
			store( devoff , off );
			return true;
		}
	}
	return false;
}
//=================================================================================================
int32 cb_call GetDeviceNum()
{
	return m_pins.GetDatanum();
}
//=================================================================================================
bool cb_call GetDeviceInfo
		(
		int32		devoff , 
		DeviceInfo*	info
		)
{
	if( devoff < 0 || devoff >= m_pins.GetDatanum() )
		return false;
	if( info == 0 )
		return true;
	info->m_name	= m_pins[devoff]->GetDeviceName();
	info->m_type	= m_pins[devoff]->GetDeviceType();
	return true;
}
//=================================================================================================
iAudioDevice cb_call CreateDevice
		(
		int32		devoff , 
		wndptr		owner
		)
{
	if( devoff < 0 || devoff >= m_pins.GetDatanum() )
		return iAudioDevice();
	return m_pins[devoff]->CreateDevice();
}
// public functions
public:
//=================================================================================================
EnumAudioDevice()
{
}
//=================================================================================================
void Reset()
{
	m_driverlist.Resize( 0 );
	m_pins.Resize( 0 );
}
};
///////////////////////////////////////////////////////////////////////////////////////////////////
// global variable define

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

};	// namespace audio_device_wdm
};	//namespace

//using namespace icubic;		

#pragma pack( pop )			//release align
