[PD-cvs] pd/portaudio/pa_asio asio.cpp,NONE,1.1.4.1 asio.h,NONE,1.1.4.1 asiodrivers.cpp,NONE,1.1.4.1 asiodrivers.h,NONE,1.1.4.1 asiodrvr.h,NONE,1.1.4.1 asiolist.cpp,NONE,1.1.4.1 asiolist.h,NONE,1.1.4.1 asiosmpl.cpp,NONE,1.1.4.1 asiosmpl.h,NONE,1.1.4.1 asiosys.h,NONE,1.1.4.1 combase.cpp,NONE,1.1.4.1 combase.h,NONE,1.1.4.1 debugmessage.cpp,NONE,1.1.4.1 dllentry.cpp,NONE,1.1.4.1 ginclude.h,NONE,1.1.4.1 iasiodrv.h,NONE,1.1.4.1 iasiothiscallresolver.cpp,NONE,1.2.2.1 iasiothiscallresolver.h,NONE,1.2.2.1 wxdebug.h,NONE,1.1.4.1 pa_asio.cpp,1.1.1.2.2.2,1.1.1.2.2.3

carmen rocco ix9 at users.sourceforge.net
Mon Sep 13 15:22:56 CEST 2004


Update of /cvsroot/pure-data/pd/portaudio/pa_asio
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv18736/portaudio/pa_asio

Modified Files:
      Tag: devel_0_37
	pa_asio.cpp 
Added Files:
      Tag: devel_0_37
	asio.cpp asio.h asiodrivers.cpp asiodrivers.h asiodrvr.h 
	asiolist.cpp asiolist.h asiosmpl.cpp asiosmpl.h asiosys.h 
	combase.cpp combase.h debugmessage.cpp dllentry.cpp ginclude.h 
	iasiodrv.h iasiothiscallresolver.cpp iasiothiscallresolver.h 
	wxdebug.h 
Log Message:
non-MSVC ASIO & multi-dev fix


--- NEW FILE: asiodrivers.h ---
#ifndef __AsioDrivers__
#define __AsioDrivers__

#include "ginclude.h"

#if MAC
#include "CodeFragments.hpp"

class AsioDrivers : public CodeFragments

#elif WINDOWS
#include <windows.h>
#include "asiolist.h"

class AsioDrivers : public AsioDriverList

#elif SGI || BEOS
#include "asiolist.h"

class AsioDrivers : public AsioDriverList

#else
#error implement me
#endif

{
public:
	AsioDrivers();
	~AsioDrivers();
	
	bool getCurrentDriverName(char *name);
	long getDriverNames(char **names, long maxDrivers);
	bool loadDriver(char *name);
	void removeCurrentDriver();
	long getCurrentDriverIndex() {return curIndex;}
protected:
	unsigned long connID;
	long curIndex;
};

#endif

--- NEW FILE: debugmessage.cpp ---
#include "asiosys.h"

#if DEBUG
#if MAC
#include <TextUtils.h>
void DEBUGGERMESSAGE(char *string)
{
	c2pstr(string);
	DebugStr((unsigned char *)string);
}
#else
#error debugmessage
#endif
#endif

--- NEW FILE: asio.cpp ---
/*
	Steinberg Audio Stream I/O API
	(c) 1996, Steinberg Soft- und Hardware GmbH

	asio.cpp
	
	asio functions entries which translate the
	asio interface to the asiodrvr class methods
*/ 
	
#include <string.h>
#include "asiosys.h"		// platform definition
#include "asio.h"

#if MAC
#include "asiodrvr.h"

#pragma export on

AsioDriver *theAsioDriver = 0;

extern "C"
{

long main()
{
	return 'ASIO';
}

#elif WINDOWS

#include "windows.h"
#include "iasiodrv.h"
#include "asiodrivers.h"

IASIO *theAsioDriver = 0;
extern AsioDrivers *asioDrivers;

#elif SGI || SUN || BEOS || LINUX
#include "asiodrvr.h"
static AsioDriver *theAsioDriver = 0;
#endif

//-----------------------------------------------------------------------------------------------------
ASIOError ASIOInit(ASIODriverInfo *info)
{
#if MAC || SGI || SUN || BEOS || LINUX
	if(theAsioDriver)
	{
		delete theAsioDriver;
		theAsioDriver = 0;
	}		
	info->driverVersion = 0;
	strcpy(info->name, "No ASIO Driver");
	theAsioDriver = getDriver();
	if(!theAsioDriver)
	{
		strcpy(info->errorMessage, "Not enough memory for the ASIO driver!"); 
		return ASE_NotPresent;
	}
	if(!theAsioDriver->init(info->sysRef))
	{
		theAsioDriver->getErrorMessage(info->errorMessage);
		delete theAsioDriver;
		theAsioDriver = 0;
		return ASE_NotPresent;
	}
	strcpy(info->errorMessage, "No ASIO Driver Error");
	theAsioDriver->getDriverName(info->name);
	info->driverVersion = theAsioDriver->getDriverVersion();
	return ASE_OK;

#else

	info->driverVersion = 0;
	strcpy(info->name, "No ASIO Driver");
	if(theAsioDriver)	// must be loaded!
	{
		if(!theAsioDriver->init(info->sysRef))
		{
			theAsioDriver->getErrorMessage(info->errorMessage);
			theAsioDriver = 0;
			return ASE_NotPresent;
		}		

		strcpy(info->errorMessage, "No ASIO Driver Error");
		theAsioDriver->getDriverName(info->name);
		info->driverVersion = theAsioDriver->getDriverVersion();
		return ASE_OK;
	}
	return ASE_NotPresent;

#endif	// !MAC
}

ASIOError ASIOExit(void)
{
	if(theAsioDriver)
	{
#if WINDOWS
		asioDrivers->removeCurrentDriver();
#else
		delete theAsioDriver;
#endif
	}		
	theAsioDriver = 0;
	return ASE_OK;
}

ASIOError ASIOStart(void)
{
	if(!theAsioDriver)
		return ASE_NotPresent;
	return theAsioDriver->start();
}

ASIOError ASIOStop(void)
{
	if(!theAsioDriver)
		return ASE_NotPresent;
	return theAsioDriver->stop();
}

ASIOError ASIOGetChannels(long *numInputChannels, long *numOutputChannels)
{
	if(!theAsioDriver)
	{
		*numInputChannels = *numOutputChannels = 0;
		return ASE_NotPresent;
	}
	return theAsioDriver->getChannels(numInputChannels, numOutputChannels);
}

ASIOError ASIOGetLatencies(long *inputLatency, long *outputLatency)
{
	if(!theAsioDriver)
	{
		*inputLatency = *outputLatency = 0;
		return ASE_NotPresent;
	}
	return theAsioDriver->getLatencies(inputLatency, outputLatency);
}

ASIOError ASIOGetBufferSize(long *minSize, long *maxSize, long *preferredSize, long *granularity)
{
	if(!theAsioDriver)
	{
		*minSize = *maxSize = *preferredSize = *granularity = 0;
		return ASE_NotPresent;
	}
	return theAsioDriver->getBufferSize(minSize, maxSize, preferredSize, granularity);
}

ASIOError ASIOCanSampleRate(ASIOSampleRate sampleRate)
{
	if(!theAsioDriver)
		return ASE_NotPresent;
	return theAsioDriver->canSampleRate(sampleRate);
}

ASIOError ASIOGetSampleRate(ASIOSampleRate *currentRate)
{
	if(!theAsioDriver)
		return ASE_NotPresent;
	return theAsioDriver->getSampleRate(currentRate);
}

ASIOError ASIOSetSampleRate(ASIOSampleRate sampleRate)
{
	if(!theAsioDriver)
		return ASE_NotPresent;
	return theAsioDriver->setSampleRate(sampleRate);
}

ASIOError ASIOGetClockSources(ASIOClockSource *clocks, long *numSources)
{
	if(!theAsioDriver)
	{
		*numSources = 0;
		return ASE_NotPresent;
	}
	return theAsioDriver->getClockSources(clocks, numSources);
}

ASIOError ASIOSetClockSource(long reference)
{
	if(!theAsioDriver)
		return ASE_NotPresent;
	return theAsioDriver->setClockSource(reference);
}

ASIOError ASIOGetSamplePosition(ASIOSamples *sPos, ASIOTimeStamp *tStamp)
{
	if(!theAsioDriver)
		return ASE_NotPresent;
	return theAsioDriver->getSamplePosition(sPos, tStamp);
}

ASIOError ASIOGetChannelInfo(ASIOChannelInfo *info)
{
	if(!theAsioDriver)
	{
		info->channelGroup = -1;
		info->type = ASIOSTInt16MSB;
		strcpy(info->name, "None");
		return ASE_NotPresent;
	}
	return theAsioDriver->getChannelInfo(info);
}

ASIOError ASIOCreateBuffers(ASIOBufferInfo *bufferInfos, long numChannels,
	long bufferSize, ASIOCallbacks *callbacks)
{
	if(!theAsioDriver)
	{
		ASIOBufferInfo *info = bufferInfos;
		for(long i = 0; i < numChannels; i++, info++)
			info->buffers[0] = info->buffers[1] = 0;
		return ASE_NotPresent;
	}
	return theAsioDriver->createBuffers(bufferInfos, numChannels, bufferSize, callbacks);
}

ASIOError ASIODisposeBuffers(void)
{
	if(!theAsioDriver)
		return ASE_NotPresent;
	return theAsioDriver->disposeBuffers();
}

ASIOError ASIOControlPanel(void)
{
	if(!theAsioDriver)
		return ASE_NotPresent;
	return theAsioDriver->controlPanel();
}

ASIOError ASIOFuture(long selector, void *opt)
{
	if(!theAsioDriver)
		return ASE_NotPresent;
	return theAsioDriver->future(selector, opt);
}

ASIOError ASIOOutputReady(void)
{
	if(!theAsioDriver)
		return ASE_NotPresent;
	return theAsioDriver->outputReady();
}

#if MAC
}	// extern "C"
#pragma export off
#endif



--- NEW FILE: asiolist.cpp ---
#include <windows.h>
#include "iasiodrv.h"
#include "asiolist.h"

#define ASIODRV_DESC		"description"
#define INPROC_SERVER		"InprocServer32"
#define ASIO_PATH			"software\\asio"
#define COM_CLSID			"clsid"

// ******************************************************************
// Local Functions 
// ******************************************************************
static LONG findDrvPath (char *clsidstr,char *dllpath,int dllpathsize)
{
	HKEY			hkEnum,hksub,hkpath;
	char			databuf[512];
	LONG 			cr,rc = -1;
	DWORD			datatype,datasize;
	DWORD			index;
	OFSTRUCT		ofs;
	HFILE			hfile;
	BOOL			found = FALSE;

	CharLowerBuff(clsidstr,strlen(clsidstr));
	if ((cr = RegOpenKey(HKEY_CLASSES_ROOT,COM_CLSID,&hkEnum)) == ERROR_SUCCESS) {

		index = 0;
		while (cr == ERROR_SUCCESS && !found) {
			cr = RegEnumKey(hkEnum,index++,(LPTSTR)databuf,512);
			if (cr == ERROR_SUCCESS) {
				CharLowerBuff(databuf,strlen(databuf));
				if (!(strcmp(databuf,clsidstr))) {
					if ((cr = RegOpenKeyEx(hkEnum,(LPCTSTR)databuf,0,KEY_READ,&hksub)) == ERROR_SUCCESS) {
						if ((cr = RegOpenKeyEx(hksub,(LPCTSTR)INPROC_SERVER,0,KEY_READ,&hkpath)) == ERROR_SUCCESS) {
							datatype = REG_SZ; datasize = (DWORD)dllpathsize;
							cr = RegQueryValueEx(hkpath,0,0,&datatype,(LPBYTE)dllpath,&datasize);
							if (cr == ERROR_SUCCESS) {
								memset(&ofs,0,sizeof(OFSTRUCT));
								ofs.cBytes = sizeof(OFSTRUCT); 
								hfile = OpenFile(dllpath,&ofs,OF_EXIST);
								if (hfile) rc = 0; 
							}
							RegCloseKey(hkpath);
						}
						RegCloseKey(hksub);
					}
					found = TRUE;	// break out 
				}
			}
		}				
		RegCloseKey(hkEnum);
	}
	return rc;
}


static LPASIODRVSTRUCT newDrvStruct (HKEY hkey,char *keyname,int drvID,LPASIODRVSTRUCT lpdrv)
{
	HKEY	hksub;
	char	databuf[256];
	char	dllpath[MAXPATHLEN];
	WORD	wData[100];
	CLSID	clsid;
	DWORD	datatype,datasize;
	LONG	cr,rc;

	if (!lpdrv) {
		if ((cr = RegOpenKeyEx(hkey,(LPCTSTR)keyname,0,KEY_READ,&hksub)) == ERROR_SUCCESS) {

			datatype = REG_SZ; datasize = 256;
			cr = RegQueryValueEx(hksub,COM_CLSID,0,&datatype,(LPBYTE)databuf,&datasize);
			if (cr == ERROR_SUCCESS) {
				rc = findDrvPath (databuf,dllpath,MAXPATHLEN);
				if (rc == 0) {
					lpdrv = new ASIODRVSTRUCT[1];
					if (lpdrv) {
						memset(lpdrv,0,sizeof(ASIODRVSTRUCT));
						lpdrv->drvID = drvID;
						MultiByteToWideChar(CP_ACP,0,(LPCSTR)databuf,-1,(LPWSTR)wData,100);
						if ((cr = CLSIDFromString((LPOLESTR)wData,(LPCLSID)&clsid)) == S_OK) {
							memcpy(&lpdrv->clsid,&clsid,sizeof(CLSID));
						}

						datatype = REG_SZ; datasize = 256;
						cr = RegQueryValueEx(hksub,ASIODRV_DESC,0,&datatype,(LPBYTE)databuf,&datasize);
						if (cr == ERROR_SUCCESS) {
							strcpy(lpdrv->drvname,databuf);
						}
						else strcpy(lpdrv->drvname,keyname);
					}
				}
			}
			RegCloseKey(hksub);
		}
	}	
	else lpdrv->next = newDrvStruct(hkey,keyname,drvID+1,lpdrv->next);

	return lpdrv;
}

static void deleteDrvStruct (LPASIODRVSTRUCT lpdrv)
{
	IASIO	*iasio;

	if (lpdrv != 0) {
		deleteDrvStruct(lpdrv->next);
		if (lpdrv->asiodrv) {
			iasio = (IASIO *)lpdrv->asiodrv;
			iasio->Release();
		}
		delete lpdrv;
	}
}


static LPASIODRVSTRUCT getDrvStruct (int drvID,LPASIODRVSTRUCT lpdrv)
{
	while (lpdrv) {
		if (lpdrv->drvID == drvID) return lpdrv;
		lpdrv = lpdrv->next;
	}
	return 0;
}
// ******************************************************************


// ******************************************************************
//	AsioDriverList
// ******************************************************************
AsioDriverList::AsioDriverList ()
{
	HKEY			hkEnum = 0;
	char			keyname[MAXDRVNAMELEN];
	LPASIODRVSTRUCT	pdl;
	LONG 			cr;
	DWORD			index = 0;
	BOOL			fin = FALSE;

	numdrv		= 0;
	lpdrvlist	= 0;

	cr = RegOpenKey(HKEY_LOCAL_MACHINE,ASIO_PATH,&hkEnum);
	while (cr == ERROR_SUCCESS) {
		if ((cr = RegEnumKey(hkEnum,index++,(LPTSTR)keyname,MAXDRVNAMELEN))== ERROR_SUCCESS) {
			lpdrvlist = newDrvStruct (hkEnum,keyname,0,lpdrvlist);
		}
		else fin = TRUE;
	}
	if (hkEnum) RegCloseKey(hkEnum);

	pdl = lpdrvlist;
	while (pdl) {
		numdrv++;
		pdl = pdl->next;
	}

	if (numdrv) CoInitialize(0);	// initialize COM
}

AsioDriverList::~AsioDriverList ()
{
	if (numdrv) {
		deleteDrvStruct(lpdrvlist);
		CoUninitialize();
	}
}


LONG AsioDriverList::asioGetNumDev (VOID)
{
	return (LONG)numdrv;
}


LONG AsioDriverList::asioOpenDriver (int drvID,LPVOID *asiodrv)
{
	LPASIODRVSTRUCT	lpdrv = 0;
	long			rc;

	if (!asiodrv) return DRVERR_INVALID_PARAM;

	if ((lpdrv = getDrvStruct(drvID,lpdrvlist)) != 0) {
		if (!lpdrv->asiodrv) {
			rc = CoCreateInstance(lpdrv->clsid,0,CLSCTX_INPROC_SERVER,lpdrv->clsid,asiodrv);
			if (rc == S_OK) {
				lpdrv->asiodrv = *asiodrv;
				return 0;
			}
			// else if (rc == REGDB_E_CLASSNOTREG)
			//	strcpy (info->messageText, "Driver not registered in the Registration Database!");
		}
		else rc = DRVERR_DEVICE_ALREADY_OPEN;
	}
	else rc = DRVERR_DEVICE_NOT_FOUND;
	
	return rc;
}


LONG AsioDriverList::asioCloseDriver (int drvID)
{
	LPASIODRVSTRUCT	lpdrv = 0;
	IASIO			*iasio;

	if ((lpdrv = getDrvStruct(drvID,lpdrvlist)) != 0) {
		if (lpdrv->asiodrv) {
			iasio = (IASIO *)lpdrv->asiodrv;
			iasio->Release();
			lpdrv->asiodrv = 0;
		}
	}

	return 0;
}

LONG AsioDriverList::asioGetDriverName (int drvID,char *drvname,int drvnamesize)
{	
	LPASIODRVSTRUCT			lpdrv = 0;

	if (!drvname) return DRVERR_INVALID_PARAM;

	if ((lpdrv = getDrvStruct(drvID,lpdrvlist)) != 0) {
		if (strlen(lpdrv->drvname) < (unsigned int)drvnamesize) {
			strcpy(drvname,lpdrv->drvname);
		}
		else {
			memcpy(drvname,lpdrv->drvname,drvnamesize-4);
			drvname[drvnamesize-4] = '.';
			drvname[drvnamesize-3] = '.';
			drvname[drvnamesize-2] = '.';
			drvname[drvnamesize-1] = 0;
		}
		return 0;
	}
	return DRVERR_DEVICE_NOT_FOUND;
}

LONG AsioDriverList::asioGetDriverPath (int drvID,char *dllpath,int dllpathsize)
{
	LPASIODRVSTRUCT			lpdrv = 0;

	if (!dllpath) return DRVERR_INVALID_PARAM;

	if ((lpdrv = getDrvStruct(drvID,lpdrvlist)) != 0) {
		if (strlen(lpdrv->dllpath) < (unsigned int)dllpathsize) {
			strcpy(dllpath,lpdrv->dllpath);
			return 0;
		}
		dllpath[0] = 0;
		return DRVERR_INVALID_PARAM;
	}
	return DRVERR_DEVICE_NOT_FOUND;
}

LONG AsioDriverList::asioGetDriverCLSID (int drvID,CLSID *clsid)
{
	LPASIODRVSTRUCT			lpdrv = 0;

	if (!clsid) return DRVERR_INVALID_PARAM;

	if ((lpdrv = getDrvStruct(drvID,lpdrvlist)) != 0) {
		memcpy(clsid,&lpdrv->clsid,sizeof(CLSID));
		return 0;
	}
	return DRVERR_DEVICE_NOT_FOUND;
}



--- NEW FILE: iasiothiscallresolver.cpp ---
/*
	IASIOThiscallResolver.cpp see the comments in iasiothiscallresolver.h for
    the top level description - this comment describes the technical details of
    the implementation.

    The latest version of this file is available from:
    http://www.audiomulch.com/~rossb/code/calliasio

    please email comments to Ross Bencina <rossb at audiomulch.com>

    BACKGROUND

    The IASIO interface declared in the Steinberg ASIO 2 SDK declares
    functions with no explicit calling convention. This causes MSVC++ to default
    to using the thiscall convention, which is a proprietary convention not
    implemented by some non-microsoft compilers - notably borland BCC,
    C++Builder, and gcc. MSVC++ is the defacto standard compiler used by
    Steinberg. As a result of this situation, the ASIO sdk will compile with
    any compiler, however attempting to execute the compiled code will cause a
    crash due to different default calling conventions on non-Microsoft
    compilers.

    IASIOThiscallResolver solves the problem by providing an adapter class that
    delegates to the IASIO interface using the correct calling convention
    (thiscall). Due to the lack of support for thiscall in the Borland and GCC
    compilers, the calls have been implemented in assembly language.

    A number of macros are defined for thiscall function calls with different
    numbers of parameters, with and without return values - it may be possible
    to modify the format of these macros to make them work with other inline
    assemblers.


    THISCALL DEFINITION

    A number of definitions of the thiscall calling convention are floating
    around the internet. The following definition has been validated against
    output from the MSVC++ compiler:

    For non-vararg functions, thiscall works as follows: the object (this)
    pointer is passed in ECX. All arguments are passed on the stack in
    right to left order. The return value is placed in EAX. The callee
    clears the passed arguments from the stack.


    FINDING FUNCTION POINTERS FROM AN IASIO POINTER

    The first field of a COM object is a pointer to its vtble. Thus a pointer
    to an object implementing the IASIO interface also points to a pointer to
    that object's vtbl. The vtble is a table of function pointers for all of
    the virtual functions exposed by the implemented interfaces.

    If we consider a variable declared as a pointer to IASO:

    IASIO *theAsioDriver

    theAsioDriver points to:

    object implementing IASIO
    {
        IASIOvtbl *vtbl
        other data
    }

    in other words, theAsioDriver points to a pointer to an IASIOvtbl

    vtbl points to a table of function pointers:

    IASIOvtbl ( interface IASIO : public IUnknown )
    {
    (IUnknown functions)
    0   virtual HRESULT STDMETHODCALLTYPE (*QueryInterface)(REFIID riid, void **ppv) = 0;
    4   virtual ULONG STDMETHODCALLTYPE (*AddRef)() = 0;
    8   virtual ULONG STDMETHODCALLTYPE (*Release)() = 0;      

    (IASIO functions)
    12	virtual ASIOBool (*init)(void *sysHandle) = 0;
    16	virtual void (*getDriverName)(char *name) = 0;
    20	virtual long (*getDriverVersion)() = 0;
    24	virtual void (*getErrorMessage)(char *string) = 0;
    28	virtual ASIOError (*start)() = 0;
    32	virtual ASIOError (*stop)() = 0;
    36	virtual ASIOError (*getChannels)(long *numInputChannels, long *numOutputChannels) = 0;
    40	virtual ASIOError (*getLatencies)(long *inputLatency, long *outputLatency) = 0;
    44	virtual ASIOError (*getBufferSize)(long *minSize, long *maxSize,
            long *preferredSize, long *granularity) = 0;
    48	virtual ASIOError (*canSampleRate)(ASIOSampleRate sampleRate) = 0;
    52	virtual ASIOError (*getSampleRate)(ASIOSampleRate *sampleRate) = 0;
    56	virtual ASIOError (*setSampleRate)(ASIOSampleRate sampleRate) = 0;
    60	virtual ASIOError (*getClockSources)(ASIOClockSource *clocks, long *numSources) = 0;
    64	virtual ASIOError (*setClockSource)(long reference) = 0;
    68	virtual ASIOError (*getSamplePosition)(ASIOSamples *sPos, ASIOTimeStamp *tStamp) = 0;
    72	virtual ASIOError (*getChannelInfo)(ASIOChannelInfo *info) = 0;
    76	virtual ASIOError (*createBuffers)(ASIOBufferInfo *bufferInfos, long numChannels,
            long bufferSize, ASIOCallbacks *callbacks) = 0;
    80	virtual ASIOError (*disposeBuffers)() = 0;
    84	virtual ASIOError (*controlPanel)() = 0;
    88	virtual ASIOError (*future)(long selector,void *opt) = 0;
    92	virtual ASIOError (*outputReady)() = 0;
    };

    The numbers in the left column show the byte offset of each function ptr
    from the beginning of the vtbl. These numbers are used in the code below
    to select different functions.

    In order to find the address of a particular function, theAsioDriver
    must first be dereferenced to find the value of the vtbl pointer:

    mov     eax, theAsioDriver
    mov     edx, [theAsioDriver]  // edx now points to vtbl[0]

    Then an offset must be added to the vtbl pointer to select a
    particular function, for example vtbl+44 points to the slot containing
    a pointer to the getBufferSize function.

    Finally vtbl+x must be dereferenced to obtain the value of the function
    pointer stored in that address:

    call    [edx+44]    // call the function pointed to by
                        // the value in the getBufferSize field of the vtbl


    SEE ALSO

    Martin Fay's OpenASIO DLL at http://www.martinfay.com solves the same
    problem by providing a new COM interface which wraps IASIO with an
    interface that uses portable calling conventions. OpenASIO must be compiled
    with MSVC, and requires that you ship the OpenASIO DLL with your
    application.

    
    ACKNOWLEDGEMENTS

    Ross Bencina: worked out the thiscall details above, wrote the original
    Borland asm macros, and a patch for asio.cpp (which is no longer needed).
    Thanks to Martin Fay for introducing me to the issues discussed here,
    and to Rene G. Ceballos for assisting with asm dumps from MSVC++.

    Antti Silvast: converted the original calliasio to work with gcc and NASM
    by implementing the asm code in a separate file.

	Fraser Adams: modified the original calliasio containing the Borland inline
    asm to add inline asm for gcc i.e. Intel syntax for Borland and AT&T syntax
    for gcc. This seems a neater approach for gcc than to have a separate .asm
    file and it means that we only need one version of the thiscall patch.

    Fraser Adams: rewrote the original calliasio patch in the form of the
    IASIOThiscallResolver class in order to avoid modifications to files from
    the Steinberg SDK, which may have had potential licence issues.

    Andrew Baldwin: contributed fixes for compatibility problems with more
    recent versions of the gcc assembler.
*/


// We only need IASIOThiscallResolver at all if we are on Win32. For other
// platforms we simply bypass the IASIOThiscallResolver definition to allow us
// to be safely #include'd whatever the platform to keep client code portable
#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__)


// If microsoft compiler we can call IASIO directly so IASIOThiscallResolver
// is not used.
#if !defined(_MSC_VER)


#include <new>
#include <assert.h>

// We have a mechanism in iasiothiscallresolver.h to ensure that asio.h is
// #include'd before it in client code, we do NOT want to do this test here.
#define iasiothiscallresolver_sourcefile 1
#include "iasiothiscallresolver.h"
#undef iasiothiscallresolver_sourcefile

// iasiothiscallresolver.h redefines ASIOInit for clients, but we don't want
// this macro defined in this translation unit.
#undef ASIOInit


// theAsioDriver is a global pointer to the current IASIO instance which the
// ASIO SDK uses to perform all actions on the IASIO interface. We substitute
// our own forwarding interface into this pointer.
extern IASIO* theAsioDriver;


// The following macros define the inline assembler for BORLAND first then gcc

#if defined(__BCPLUSPLUS__) || defined(__BORLANDC__)          


#define CALL_THISCALL_0( resultName, thisPtr, funcOffset )\
    void *this_ = (thisPtr);                                                \
    __asm {                                                                 \
        mov     ecx, this_            ;                                     \
        mov     eax, [ecx]            ;                                     \
        call    [eax+funcOffset]      ;                                     \
        mov     resultName, eax       ;                                     \
    }


#define CALL_VOID_THISCALL_1( thisPtr, funcOffset, param1 )\
    void *this_ = (thisPtr);                                                \
    __asm {                                                                 \
        mov     eax, param1           ;                                     \
        push    eax                   ;                                     \
        mov     ecx, this_            ;                                     \
        mov     eax, [ecx]            ;                                     \
        call    [eax+funcOffset]      ;                                     \
    }


#define CALL_THISCALL_1( resultName, thisPtr, funcOffset, param1 )\
    void *this_ = (thisPtr);                                                \
    __asm {                                                                 \
        mov     eax, param1           ;                                     \
        push    eax                   ;                                     \
        mov     ecx, this_            ;                                     \
        mov     eax, [ecx]            ;                                     \
        call    [eax+funcOffset]      ;                                     \
        mov     resultName, eax       ;                                     \
    }


#define CALL_THISCALL_1_DOUBLE( resultName, thisPtr, funcOffset, param1 )\
    void *this_ = (thisPtr);                                                \
    void *doubleParamPtr_ (&param1);                                        \
    __asm {                                                                 \
        mov     eax, doubleParamPtr_  ;                                     \
        push    [eax+4]               ;                                     \
        push    [eax]                 ;                                     \
        mov     ecx, this_            ;                                     \
        mov     eax, [ecx]            ;                                     \
        call    [eax+funcOffset]      ;                                     \
        mov     resultName, eax       ;                                     \
    }


#define CALL_THISCALL_2( resultName, thisPtr, funcOffset, param1, param2 )\
    void *this_ = (thisPtr);                                                \
    __asm {                                                                 \
        mov     eax, param2           ;                                     \
        push    eax                   ;                                     \
        mov     eax, param1           ;                                     \
        push    eax                   ;                                     \
        mov     ecx, this_            ;                                     \
        mov     eax, [ecx]            ;                                     \
        call    [eax+funcOffset]      ;                                     \
        mov     resultName, eax       ;                                     \
    }


#define CALL_THISCALL_4( resultName, thisPtr, funcOffset, param1, param2, param3, param4 )\
    void *this_ = (thisPtr);                                                \
    __asm {                                                                 \
        mov     eax, param4           ;                                     \
        push    eax                   ;                                     \
        mov     eax, param3           ;                                     \
        push    eax                   ;                                     \
        mov     eax, param2           ;                                     \
        push    eax                   ;                                     \
        mov     eax, param1           ;                                     \
        push    eax                   ;                                     \
        mov     ecx, this_            ;                                     \
        mov     eax, [ecx]            ;                                     \
        call    [eax+funcOffset]      ;                                     \
        mov     resultName, eax       ;                                     \
    }


#elif defined(__GNUC__)


#define CALL_THISCALL_0( resultName, thisPtr, funcOffset )                  \
    __asm__ __volatile__ ("movl (%1), %%edx\n\t"                            \
                          "call *"#funcOffset"(%%edx)\n\t"                  \
                          :"=a"(resultName) /* Output Operands */           \
                          :"c"(thisPtr)     /* Input Operands */            \
                         );                                                 \


#define CALL_VOID_THISCALL_1( thisPtr, funcOffset, param1 )                 \
    __asm__ __volatile__ ("pushl %0\n\t"                                    \
                          "movl (%1), %%edx\n\t"                            \
                          "call *"#funcOffset"(%%edx)\n\t"                  \
                          :                 /* Output Operands */           \
                          :"r"(param1),     /* Input Operands */            \
                           "c"(thisPtr)                                     \
                         );                                                 \


#define CALL_THISCALL_1( resultName, thisPtr, funcOffset, param1 )          \
    __asm__ __volatile__ ("pushl %1\n\t"                                    \
                          "movl (%2), %%edx\n\t"                            \
                          "call *"#funcOffset"(%%edx)\n\t"                  \
                          :"=a"(resultName) /* Output Operands */           \
                          :"r"(param1),     /* Input Operands */            \
                           "c"(thisPtr)                                     \
                          );                                                \


#define CALL_THISCALL_1_DOUBLE( resultName, thisPtr, funcOffset, param1 )   \
    __asm__ __volatile__ ("pushl 4(%1)\n\t"                                 \
                          "pushl (%1)\n\t"                                  \
                          "movl (%2), %%edx\n\t"                            \
                          "call *"#funcOffset"(%%edx);\n\t"                 \
                          :"=a"(resultName) /* Output Operands */           \
                          :"a"(&param1),    /* Input Operands */            \
                           /* Note: Using "r" above instead of "a" fails */ \
                           /* when using GCC 3.3.3, and maybe later versions*/\
                           "c"(thisPtr)                                     \
                          );                                                \


#define CALL_THISCALL_2( resultName, thisPtr, funcOffset, param1, param2 )  \
    __asm__ __volatile__ ("pushl %1\n\t"                                    \
                          "pushl %2\n\t"                                    \
                          "movl (%3), %%edx\n\t"                            \
                          "call *"#funcOffset"(%%edx)\n\t"                  \
                          :"=a"(resultName) /* Output Operands */           \
                          :"r"(param2),     /* Input Operands */            \
                           "r"(param1),                                     \
                           "c"(thisPtr)                                     \
                          );                                                \


#define CALL_THISCALL_4( resultName, thisPtr, funcOffset, param1, param2, param3, param4 )\
    __asm__ __volatile__ ("pushl %1\n\t"                                    \
                          "pushl %2\n\t"                                    \
                          "pushl %3\n\t"                                    \
                          "pushl %4\n\t"                                    \
                          "movl (%5), %%edx\n\t"                            \
                          "call *"#funcOffset"(%%edx)\n\t"                  \
                          :"=a"(resultName) /* Output Operands */           \
                          :"r"(param4),     /* Input Operands  */           \
                           "r"(param3),                                     \
                           "r"(param2),                                     \
                           "r"(param1),                                     \
                           "c"(thisPtr)                                     \
                          );                                                \

#endif



// Our static singleton instance.
IASIOThiscallResolver IASIOThiscallResolver::instance;

// Constructor called to initialize static Singleton instance above. Note that
// it is important not to clear that_ incase it has already been set by the call
// to placement new in ASIOInit().
IASIOThiscallResolver::IASIOThiscallResolver()
{
}

// Constructor called from ASIOInit() below
IASIOThiscallResolver::IASIOThiscallResolver(IASIO* that)
: that_( that )
{
}

// Implement IUnknown methods as assert(false). IASIOThiscallResolver is not
// really a COM object, just a wrapper which will work with the ASIO SDK.
// If you wanted to use ASIO without the SDK you might want to implement COM
// aggregation in these methods.
HRESULT STDMETHODCALLTYPE IASIOThiscallResolver::QueryInterface(REFIID riid, void **ppv)
{
    (void)riid;     // suppress unused variable warning

    assert( false ); // this function should never be called by the ASIO SDK.

    *ppv = NULL;
    return E_NOINTERFACE;
}

ULONG STDMETHODCALLTYPE IASIOThiscallResolver::AddRef()
{
    assert( false ); // this function should never be called by the ASIO SDK.

    return 1;
}

ULONG STDMETHODCALLTYPE IASIOThiscallResolver::Release()
{
    assert( false ); // this function should never be called by the ASIO SDK.
    
    return 1;
}


// Implement the IASIO interface methods by performing the vptr manipulation
// described above then delegating to the real implementation.
ASIOBool IASIOThiscallResolver::init(void *sysHandle)
{
    ASIOBool result;
    CALL_THISCALL_1( result, that_, 12, sysHandle );
    return result;
}

void IASIOThiscallResolver::getDriverName(char *name)
{
    CALL_VOID_THISCALL_1( that_, 16, name );
}

long IASIOThiscallResolver::getDriverVersion()
{
    ASIOBool result;
    CALL_THISCALL_0( result, that_, 20 );
    return result;
}

void IASIOThiscallResolver::getErrorMessage(char *string)
{
     CALL_VOID_THISCALL_1( that_, 24, string );
}

ASIOError IASIOThiscallResolver::start()
{
    ASIOBool result;
    CALL_THISCALL_0( result, that_, 28 );
    return result;
}

ASIOError IASIOThiscallResolver::stop()
{
    ASIOBool result;
    CALL_THISCALL_0( result, that_, 32 );
    return result;
}

ASIOError IASIOThiscallResolver::getChannels(long *numInputChannels, long *numOutputChannels)
{
    ASIOBool result;
    CALL_THISCALL_2( result, that_, 36, numInputChannels, numOutputChannels );
    return result;
}

ASIOError IASIOThiscallResolver::getLatencies(long *inputLatency, long *outputLatency)
{
    ASIOBool result;
    CALL_THISCALL_2( result, that_, 40, inputLatency, outputLatency );
    return result;
}

ASIOError IASIOThiscallResolver::getBufferSize(long *minSize, long *maxSize,
        long *preferredSize, long *granularity)
{
    ASIOBool result;
    CALL_THISCALL_4( result, that_, 44, minSize, maxSize, preferredSize, granularity );
    return result;
}

ASIOError IASIOThiscallResolver::canSampleRate(ASIOSampleRate sampleRate)
{
    ASIOBool result;
    CALL_THISCALL_1_DOUBLE( result, that_, 48, sampleRate );
    return result;
}

ASIOError IASIOThiscallResolver::getSampleRate(ASIOSampleRate *sampleRate)
{
    ASIOBool result;
    CALL_THISCALL_1( result, that_, 52, sampleRate );
    return result;
}

ASIOError IASIOThiscallResolver::setSampleRate(ASIOSampleRate sampleRate)
{    
    ASIOBool result;
    CALL_THISCALL_1_DOUBLE( result, that_, 56, sampleRate );
    return result;
}

ASIOError IASIOThiscallResolver::getClockSources(ASIOClockSource *clocks, long *numSources)
{
    ASIOBool result;
    CALL_THISCALL_2( result, that_, 60, clocks, numSources );
    return result;
}

ASIOError IASIOThiscallResolver::setClockSource(long reference)
{
    ASIOBool result;
    CALL_THISCALL_1( result, that_, 64, reference );
    return result;
}

ASIOError IASIOThiscallResolver::getSamplePosition(ASIOSamples *sPos, ASIOTimeStamp *tStamp)
{
    ASIOBool result;
    CALL_THISCALL_2( result, that_, 68, sPos, tStamp );
    return result;
}

ASIOError IASIOThiscallResolver::getChannelInfo(ASIOChannelInfo *info)
{
    ASIOBool result;
    CALL_THISCALL_1( result, that_, 72, info );
    return result;
}

ASIOError IASIOThiscallResolver::createBuffers(ASIOBufferInfo *bufferInfos,
        long numChannels, long bufferSize, ASIOCallbacks *callbacks)
{
    ASIOBool result;
    CALL_THISCALL_4( result, that_, 76, bufferInfos, numChannels, bufferSize, callbacks );
    return result;
}

ASIOError IASIOThiscallResolver::disposeBuffers()
{
    ASIOBool result;
    CALL_THISCALL_0( result, that_, 80 );
    return result;
}

ASIOError IASIOThiscallResolver::controlPanel()
{
    ASIOBool result;
    CALL_THISCALL_0( result, that_, 84 );
    return result;
}

ASIOError IASIOThiscallResolver::future(long selector,void *opt)
{
    ASIOBool result;
    CALL_THISCALL_2( result, that_, 88, selector, opt );
    return result;
}

ASIOError IASIOThiscallResolver::outputReady()
{
    ASIOBool result;
    CALL_THISCALL_0( result, that_, 92 );
    return result;
}


// Implement our substitute ASIOInit() method
ASIOError IASIOThiscallResolver::ASIOInit(ASIODriverInfo *info)
{
    // To ensure that our instance's vptr is correctly constructed, even if
    // ASIOInit is called prior to main(), we explicitly call its constructor
    // (potentially over the top of an existing instance). Note that this is
    // pretty ugly, and is only safe because IASIOThiscallResolver has no
    // destructor and contains no objects with destructors.
    new((void*)&instance) IASIOThiscallResolver( theAsioDriver );

    // Interpose between ASIO client code and the real driver.
    theAsioDriver = &instance;

    // Note that we never need to switch theAsioDriver back to point to the
    // real driver because theAsioDriver is reset to zero in ASIOExit().

    // Delegate to the real ASIOInit
	return ::ASIOInit(info);
}


#endif /* !defined(_MSC_VER) */

#endif /* Win32 */


--- NEW FILE: asio.h ---
//---------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------

/*
	Steinberg Audio Stream I/O API
	(c) 1997 - 1999, Steinberg Soft- und Hardware GmbH

	ASIO Interface Specification v 2.0

	basic concept is an i/o synchronous double-buffer scheme:
	
	on bufferSwitch(index == 0), host will read/write:

		after ASIOStart(), the
  read  first input buffer A (index 0)
	|   will be invalid (empty)
	*   ------------------------
	|------------------------|-----------------------|
	|                        |                       |
	|  Input Buffer A (0)    |   Input Buffer B (1)  |
	|                        |                       |
	|------------------------|-----------------------|
	|                        |                       |
	|  Output Buffer A (0)   |   Output Buffer B (1) |
	|                        |                       |
	|------------------------|-----------------------|
	*                        -------------------------
	|                        before calling ASIOStart(),
  write                      host will have filled output
                             buffer B (index 1) already

  *please* take special care of proper statement of input
  and output latencies (see ASIOGetLatencies()), these
  control sequencer sync accuracy

*/

//---------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------

/*

prototypes summary:

ASIOError ASIOInit(ASIODriverInfo *info);
ASIOError ASIOExit(void);
ASIOError ASIOStart(void);
ASIOError ASIOStop(void);
ASIOError ASIOGetChannels(long *numInputChannels, long *numOutputChannels);
ASIOError ASIOGetLatencies(long *inputLatency, long *outputLatency);
ASIOError ASIOGetBufferSize(long *minSize, long *maxSize, long *preferredSize, long *granularity);
ASIOError ASIOCanSampleRate(ASIOSampleRate sampleRate);
ASIOError ASIOGetSampleRate(ASIOSampleRate *currentRate);
ASIOError ASIOSetSampleRate(ASIOSampleRate sampleRate);
ASIOError ASIOGetClockSources(ASIOClockSource *clocks, long *numSources);
ASIOError ASIOSetClockSource(long reference);
ASIOError ASIOGetSamplePosition (ASIOSamples *sPos, ASIOTimeStamp *tStamp);
ASIOError ASIOGetChannelInfo(ASIOChannelInfo *info);
ASIOError ASIOCreateBuffers(ASIOBufferInfo *bufferInfos, long numChannels,
	long bufferSize, ASIOCallbacks *callbacks);
ASIOError ASIODisposeBuffers(void);
ASIOError ASIOControlPanel(void);
void *ASIOFuture(long selector, void *params);
ASIOError ASIOOutputReady(void);

*/

//---------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------

#ifndef __ASIO_H
#define __ASIO_H

// force 4 byte alignment
#if defined(_MSC_VER) && !defined(__MWERKS__) 
#pragma pack(push,4)
#elif PRAGMA_ALIGN_SUPPORTED
#pragma options align = native
#endif

//- - - - - - - - - - - - - - - - - - - - - - - - -
// Type definitions
//- - - - - - - - - - - - - - - - - - - - - - - - -

// number of samples data type is 64 bit integer
#if NATIVE_INT64
	typedef long long int ASIOSamples;
#else
	typedef struct ASIOSamples {
		unsigned long hi;
		unsigned long lo;
	} ASIOSamples;
#endif

// Timestamp data type is 64 bit integer,
// Time format is Nanoseconds.
#if NATIVE_INT64
	typedef long long int ASIOTimeStamp ;
#else
	typedef struct ASIOTimeStamp {
		unsigned long hi;
		unsigned long lo;
	} ASIOTimeStamp;
#endif

// Samplerates are expressed in IEEE 754 64 bit double float,
// native format as host computer
#if IEEE754_64FLOAT
	typedef double ASIOSampleRate;
#else
	typedef struct ASIOSampleRate {
		char ieee[8];
	} ASIOSampleRate;
#endif

// Boolean values are expressed as long
typedef long ASIOBool;
enum {
	ASIOFalse = 0,
	ASIOTrue = 1
};

// Sample Types are expressed as long
typedef long ASIOSampleType;
enum {
	ASIOSTInt16MSB   = 0,
	ASIOSTInt24MSB   = 1,		// used for 20 bits as well
	ASIOSTInt32MSB   = 2,
	ASIOSTFloat32MSB = 3,		// IEEE 754 32 bit float
	ASIOSTFloat64MSB = 4,		// IEEE 754 64 bit double float

	// these are used for 32 bit data buffer, with different alignment of the data inside
	// 32 bit PCI bus systems can be more easily used with these
	ASIOSTInt32MSB16 = 8,		// 32 bit data with 18 bit alignment
	ASIOSTInt32MSB18 = 9,		// 32 bit data with 18 bit alignment
	ASIOSTInt32MSB20 = 10,		// 32 bit data with 20 bit alignment
	ASIOSTInt32MSB24 = 11,		// 32 bit data with 24 bit alignment
	
	ASIOSTInt16LSB   = 16,
	ASIOSTInt24LSB   = 17,		// used for 20 bits as well
	ASIOSTInt32LSB   = 18,
	ASIOSTFloat32LSB = 19,		// IEEE 754 32 bit float, as found on Intel x86 architecture
	ASIOSTFloat64LSB = 20, 		// IEEE 754 64 bit double float, as found on Intel x86 architecture

	// these are used for 32 bit data buffer, with different alignment of the data inside
	// 32 bit PCI bus systems can more easily used with these
	ASIOSTInt32LSB16 = 24,		// 32 bit data with 18 bit alignment
	ASIOSTInt32LSB18 = 25,		// 32 bit data with 18 bit alignment
	ASIOSTInt32LSB20 = 26,		// 32 bit data with 20 bit alignment
	ASIOSTInt32LSB24 = 27		// 32 bit data with 24 bit alignment
};

//- - - - - - - - - - - - - - - - - - - - - - - - -
// Error codes
//- - - - - - - - - - - - - - - - - - - - - - - - -

typedef long ASIOError;
enum {
	ASE_OK = 0,             // This value will be returned whenever the call succeeded
	ASE_SUCCESS = 0x3f4847a0,	// unique success return value for ASIOFuture calls
	ASE_NotPresent = -1000, // hardware input or output is not present or available
	ASE_HWMalfunction,      // hardware is malfunctioning (can be returned by any ASIO function)
	ASE_InvalidParameter,   // input parameter invalid
	ASE_InvalidMode,        // hardware is in a bad mode or used in a bad mode
	ASE_SPNotAdvancing,     // hardware is not running when sample position is inquired
	ASE_NoClock,            // sample clock or rate cannot be determined or is not present
	ASE_NoMemory            // not enough memory for completing the request
};

//---------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------

//- - - - - - - - - - - - - - - - - - - - - - - - -
// Time Info support
//- - - - - - - - - - - - - - - - - - - - - - - - -

typedef struct ASIOTimeCode
{       
	double          speed;                  // speed relation (fraction of nominal speed)
	                                        // optional; set to 0. or 1. if not supported
	ASIOSamples     timeCodeSamples;        // time in samples
	unsigned long   flags;                  // some information flags (see below)
	char future[64];
} ASIOTimeCode;

typedef enum ASIOTimeCodeFlags
{
	kTcValid                = 1,
	kTcRunning              = 1 << 1,
	kTcReverse              = 1 << 2,
	kTcOnspeed              = 1 << 3,
	kTcStill                = 1 << 4,
	
	kTcSpeedValid           = 1 << 8
}  ASIOTimeCodeFlags;

typedef struct AsioTimeInfo
{
	double          speed;                  // absolute speed (1. = nominal)
	ASIOTimeStamp   systemTime;             // system time related to samplePosition, in nanoseconds
	                                        // on mac, must be derived from Microseconds() (not UpTime()!)
	                                        // on windows, must be derived from timeGetTime()
	ASIOSamples     samplePosition;
	ASIOSampleRate  sampleRate;             // current rate
	unsigned long flags;                    // (see below)
	char reserved[12];
} AsioTimeInfo;

typedef enum AsioTimeInfoFlags
{
	kSystemTimeValid        = 1,            // must always be valid
	kSamplePositionValid    = 1 << 1,       // must always be valid
	kSampleRateValid        = 1 << 2,
	kSpeedValid             = 1 << 3,
	
	kSampleRateChanged      = 1 << 4,
	kClockSourceChanged     = 1 << 5
} AsioTimeInfoFlags;

typedef struct ASIOTime                          // both input/output
{
	long reserved[4];                       // must be 0
	struct AsioTimeInfo     timeInfo;       // required
	struct ASIOTimeCode     timeCode;       // optional, evaluated if (timeCode.flags & kTcValid)
} ASIOTime;

/*

using time info:
it is recommended to use the new method with time info even if the asio
device does not support timecode; continuous calls to ASIOGetSamplePosition
and ASIOGetSampleRate are avoided, and there is a more defined relationship
between callback time and the time info.

see the example below.
to initiate time info mode, after you have received the callbacks pointer in
ASIOCreateBuffers, you will call the asioMessage callback with kAsioSupportsTimeInfo
as the argument. if this returns 1, host has accepted time info mode.
now host expects the new callback bufferSwitchTimeInfo to be used instead
of the old bufferSwitch method. the ASIOTime structure is assumed to be valid
and accessible until the callback returns.

using time code:
if the device supports reading time code, it will call host's asioMessage callback
with kAsioSupportsTimeCode as the selector. it may then fill the according
fields and set the kTcValid flag.
host will call the future method with the kAsioEnableTimeCodeRead selector when
it wants to enable or disable tc reading by the device. you should also support
the kAsioCanTimeInfo and kAsioCanTimeCode selectors in ASIOFuture (see example).

note:
the AsioTimeInfo/ASIOTimeCode pair is supposed to work in both directions.
as a matter of convention, the relationship between the sample
position counter and the time code at buffer switch time is
(ignoring offset between tc and sample pos when tc is running):

on input:	sample 0 -> input  buffer sample 0 -> time code 0
on output:	sample 0 -> output buffer sample 0 -> time code 0

this means that for 'real' calculations, one has to take into account
the according latencies.

example:

ASIOTime asioTime;

in createBuffers()
{
	memset(&asioTime, 0, sizeof(ASIOTime));
	AsioTimeInfo* ti = &asioTime.timeInfo;
	ti->sampleRate = theSampleRate;
	ASIOTimeCode* tc = &asioTime.timeCode;
	tc->speed = 1.;
	timeInfoMode = false;
	canTimeCode = false;
	if(callbacks->asioMessage(kAsioSupportsTimeInfo, 0, 0, 0) == 1)
	{
		timeInfoMode = true;
#if kCanTimeCode
		if(callbacks->asioMessage(kAsioSupportsTimeCode, 0, 0, 0) == 1)
			canTimeCode = true;
#endif
	}
}

void switchBuffers(long doubleBufferIndex, bool processNow)
{
	if(timeInfoMode)
	{
		AsioTimeInfo* ti = &asioTime.timeInfo;
		ti->flags =	kSystemTimeValid | kSamplePositionValid | kSampleRateValid;
		ti->systemTime = theNanoSeconds;
		ti->samplePosition = theSamplePosition;
		if(ti->sampleRate != theSampleRate)
			ti->flags |= kSampleRateChanged;
		ti->sampleRate = theSampleRate;

#if kCanTimeCode
		if(canTimeCode && timeCodeEnabled)
		{
			ASIOTimeCode* tc = &asioTime.timeCode;
			tc->timeCodeSamples = tcSamples;						// tc in samples
			tc->flags = kTcValid | kTcRunning | kTcOnspeed;			// if so...
		}
		ASIOTime* bb = callbacks->bufferSwitchTimeInfo(&asioTime, doubleBufferIndex, processNow ? ASIOTrue : ASIOFalse);
#else
		callbacks->bufferSwitchTimeInfo(&asioTime, doubleBufferIndex, processNow ? ASIOTrue : ASIOFalse);
#endif
	}
	else
		callbacks->bufferSwitch(doubleBufferIndex, ASIOFalse);
}

ASIOError ASIOFuture(long selector, void *params)
{
	switch(selector)
	{
		case kAsioEnableTimeCodeRead:
			timeCodeEnabled = true;
			return ASE_SUCCESS;
		case kAsioDisableTimeCodeRead:
			timeCodeEnabled = false;
			return ASE_SUCCESS;
		case kAsioCanTimeInfo:
			return ASE_SUCCESS;
		#if kCanTimeCode
		case kAsioCanTimeCode:
			return ASE_SUCCESS;
		#endif
	}
	return ASE_NotPresent;
};

*/

//- - - - - - - - - - - - - - - - - - - - - - - - -
// application's audio stream handler callbacks
//- - - - - - - - - - - - - - - - - - - - - - - - -

typedef struct ASIOCallbacks
{
	void (*bufferSwitch) (long doubleBufferIndex, ASIOBool directProcess);
		// bufferSwitch indicates that both input and output are to be processed.
		// the current buffer half index (0 for A, 1 for B) determines
		// - the output buffer that the host should start to fill. the other buffer
		//   will be passed to output hardware regardless of whether it got filled
		//   in time or not.
		// - the input buffer that is now filled with incoming data. Note that
		//   because of the synchronicity of i/o, the input always has at
		//   least one buffer latency in relation to the output.
		// directProcess suggests to the host whether it should immedeately
		// start processing (directProcess == ASIOTrue), or whether its process
		// should be deferred because the call comes from a very low level
		// (for instance, a high level priority interrupt), and direct processing
		// would cause timing instabilities for the rest of the system. If in doubt,
		// directProcess should be set to ASIOFalse.
		// Note: bufferSwitch may be called at interrupt time for highest efficiency.

	void (*sampleRateDidChange) (ASIOSampleRate sRate);
		// gets called when the AudioStreamIO detects a sample rate change
		// If sample rate is unknown, 0 is passed (for instance, clock loss
		// when externally synchronized).

	long (*asioMessage) (long selector, long value, void* message, double* opt);
		// generic callback for various purposes, see selectors below.
		// note this is only present if the asio version is 2 or higher

	ASIOTime* (*bufferSwitchTimeInfo) (ASIOTime* params, long doubleBufferIndex, ASIOBool directProcess);
		// new callback with time info. makes ASIOGetSamplePosition() and various
		// calls to ASIOGetSampleRate obsolete,
		// and allows for timecode sync etc. to be preferred; will be used if
		// the driver calls asioMessage with selector kAsioSupportsTimeInfo.
} ASIOCallbacks;

// asioMessage selectors
enum
{
	kAsioSelectorSupported = 1,	// selector in <value>, returns 1L if supported,
								// 0 otherwise
    kAsioEngineVersion,			// returns engine (host) asio implementation version,
								// 2 or higher
	kAsioResetRequest,			// request driver reset. if accepted, this
								// will close the driver (ASIO_Exit() ) and
								// re-open it again (ASIO_Init() etc). some
								// drivers need to reconfigure for instance
								// when the sample rate changes, or some basic
								// changes have been made in ASIO_ControlPanel().
								// returns 1L; note the request is merely passed
								// to the application, there is no way to determine
								// if it gets accepted at this time (but it usually
								// will be).
	kAsioBufferSizeChange,		// not yet supported, will currently always return 0L.
								// for now, use kAsioResetRequest instead.
								// once implemented, the new buffer size is expected
								// in <value>, and on success returns 1L
	kAsioResyncRequest,			// the driver went out of sync, such that
								// the timestamp is no longer valid. this
								// is a request to re-start the engine and
								// slave devices (sequencer). returns 1 for ok,
								// 0 if not supported.
	kAsioLatenciesChanged, 		// the drivers latencies have changed. The engine
								// will refetch the latencies.
	kAsioSupportsTimeInfo,		// if host returns true here, it will expect the
								// callback bufferSwitchTimeInfo to be called instead
								// of bufferSwitch
	kAsioSupportsTimeCode,		// supports time code reading/writing

	kAsioSupportsInputMonitor,	// supports input monitoring

	kAsioNumMessageSelectors
};

//---------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------

//- - - - - - - - - - - - - - - - - - - - - - - - -
// (De-)Construction
//- - - - - - - - - - - - - - - - - - - - - - - - -

typedef struct ASIODriverInfo
{
	long asioVersion;		// currently, 2
	long driverVersion;		// driver specific
	char name[32];
	char errorMessage[124];
	void *sysRef;			// on input: system reference
							// (Windows: application main window handle, Mac & SGI: 0)
} ASIODriverInfo;

ASIOError ASIOInit(ASIODriverInfo *info);
/* Purpose:
	  Initialize the AudioStreamIO.
	Parameter:
	  info: pointer to an ASIODriver structure:
	    - asioVersion:
			- on input, the host version. *** Note *** this is 0 for earlier asio
			implementations, and the asioMessage callback is implemeted
			only if asioVersion is 2 or greater. sorry but due to a design fault
			the driver doesn't have access to the host version in ASIOInit :-(
			added selector for host (engine) version in the asioMessage callback
			so we're ok from now on.
			- on return, asio implementation version.
			  older versions are 1
			  if you support this version (namely, ASIO_outputReady() )
			  this should be 2 or higher. also see the note in
			  ASIO_getTimeStamp() !
	    - version: on return, the driver version (format is driver specific)
	    - name: on return, a null-terminated string containing the driver's name
		- error message: on return, should contain a user message describing
		  the type of error that occured during ASIOInit(), if any.
		- sysRef: platform specific
	Returns:
	  If neither input nor output is present ASE_NotPresent
	  will be returned.
	  ASE_NoMemory, ASE_HWMalfunction are other possible error conditions
*/

ASIOError ASIOExit(void);
/* Purpose:
	  Terminates the AudioStreamIO.
	Parameter:
	  None.
	Returns:
	  If neither input nor output is present ASE_NotPresent
	  will be returned.
	Notes: this implies ASIOStop() and ASIODisposeBuffers(),
	  meaning that no host callbacks must be accessed after ASIOExit().
*/

//- - - - - - - - - - - - - - - - - - - - - - - - -
// Start/Stop
//- - - - - - - - - - - - - - - - - - - - - - - - -

ASIOError ASIOStart(void);
/* Purpose:
	  Start input and output processing synchronously.
	  This will
	  - reset the sample counter to zero
	  - start the hardware (both input and output)
	    The first call to the hosts' bufferSwitch(index == 0) then tells
	    the host to read from input buffer A (index 0), and start
	    processing to output buffer A while output buffer B (which
	    has been filled by the host prior to calling ASIOStart())
	    is possibly sounding (see also ASIOGetLatencies()) 
	Parameter:
	  None.
	Returns:
	  If neither input nor output is present, ASE_NotPresent
	  will be returned.
	  If the hardware fails to start, ASE_HWMalfunction will be returned.
	Notes:
	  There is no restriction on the time that ASIOStart() takes
	  to perform (that is, it is not considered a realtime trigger).
*/

ASIOError ASIOStop(void);
/* Purpose:
	  Stops input and output processing altogether.
	Parameter:
	  None.
	Returns:
	  If neither input nor output is present ASE_NotPresent
	  will be returned.
	Notes:
	  On return from ASIOStop(), the driver must in no
	  case call the hosts' bufferSwitch() routine.
*/

//- - - - - - - - - - - - - - - - - - - - - - - - -
// Inquiry methods and sample rate
//- - - - - - - - - - - - - - - - - - - - - - - - -

ASIOError ASIOGetChannels(long *numInputChannels, long *numOutputChannels);
/* Purpose:
	  Returns number of individual input/output channels.
	Parameter:
	  numInputChannels will hold the number of available input channels
	  numOutputChannels will hold the number of available output channels
	Returns:
	  If no input/output is present ASE_NotPresent will be returned.
	  If only inputs, or only outputs are available, the according
	  other parameter will be zero, and ASE_OK is returned.
*/

ASIOError ASIOGetLatencies(long *inputLatency, long *outputLatency);
/* Purpose:
	  Returns the input and output latencies. This includes
	  device specific delays, like FIFOs etc.
	Parameter:
	  inputLatency will hold the 'age' of the first sample frame
	  in the input buffer when the hosts reads it in bufferSwitch()
	  (this is theoretical, meaning it does not include the overhead
	  and delay between the actual physical switch, and the time
	  when bufferSitch() enters).
	  This will usually be the size of one block in sample frames, plus
	  device specific latencies.

	  outputLatency will specify the time between the buffer switch,
	  and the time when the next play buffer will start to sound.
	  The next play buffer is defined as the one the host starts
	  processing after (or at) bufferSwitch(), indicated by the
	  index parameter (0 for buffer A, 1 for buffer B).
	  It will usually be either one block, if the host writes directly
	  to a dma buffer, or two or more blocks if the buffer is 'latched' by
	  the driver. As an example, on ASIOStart(), the host will have filled
	  the play buffer at index 1 already; when it gets the callback (with
	  the parameter index == 0), this tells it to read from the input
	  buffer 0, and start to fill the play buffer 0 (assuming that now
	  play buffer 1 is already sounding). In this case, the output
	  latency is one block. If the driver decides to copy buffer 1
	  at that time, and pass it to the hardware at the next slot (which
	  is most commonly done, but should be avoided), the output latency
	  becomes two blocks instead, resulting in a total i/o latency of at least
	  3 blocks. As memory access is the main bottleneck in native dsp processing,
	  and to acheive less latency, it is highly recommended to try to avoid
	  copying (this is also why the driver is the owner of the buffers). To
	  summarize, the minimum i/o latency can be acheived if the input buffer
	  is processed by the host into the output buffer which will physically
	  start to sound on the next time slice. Also note that the host expects
	  the bufferSwitch() callback to be accessed for each time slice in order
	  to retain sync, possibly recursively; if it fails to process a block in
	  time, it will suspend its operation for some time in order to recover.
	Returns:
	  If no input/output is present ASE_NotPresent will be returned.
*/

ASIOError ASIOGetBufferSize(long *minSize, long *maxSize, long *preferredSize, long *granularity);
/* Purpose:
	  Returns min, max, and preferred buffer sizes for input/output
	Parameter:
	  minSize will hold the minimum buffer size
	  maxSize will hold the maxium possible buffer size
	  preferredSize will hold the preferred buffer size (a size which
	  best fits performance and hardware requirements)
	  granularity will hold the granularity at which buffer sizes
	  may differ. Usually, the buffer size will be a power of 2;
	  in this case, granularity will hold -1 on return, signalling
	  possible buffer sizes starting from minSize, increased in
	  powers of 2 up to maxSize.
	Returns:
	  If no input/output is present ASE_NotPresent will be returned.
	Notes:
	  When minimum and maximum buffer size are equal,
	  the preferred buffer size has to be the same value as well; granularity
	  should be 0 in this case.
*/

ASIOError ASIOCanSampleRate(ASIOSampleRate sampleRate);
/* Purpose:
	  Inquires the hardware for the available sample rates.
	Parameter:
	  sampleRate is the rate in question.
	Returns:
	  If the inquired sample rate is not supported, ASE_NoClock will be returned.
	  If no input/output is present ASE_NotPresent will be returned.
*/
ASIOError ASIOGetSampleRate(ASIOSampleRate *currentRate);
/* Purpose:
	  Get the current sample Rate.
	Parameter:
	  currentRate will hold the current sample rate on return.
	Returns:
	  If sample rate is unknown, sampleRate will be 0 and ASE_NoClock will be returned.
	  If no input/output is present ASE_NotPresent will be returned.
	Notes:
*/

ASIOError ASIOSetSampleRate(ASIOSampleRate sampleRate);
/* Purpose:
	  Set the hardware to the requested sample Rate. If sampleRate == 0,
	  enable external sync.
	Parameter:
	  sampleRate: on input, the requested rate
	Returns:
	  If sampleRate is unknown ASE_NoClock will be returned.
	  If the current clock is external, and sampleRate is != 0,
	  ASE_InvalidMode will be returned
	  If no input/output is present ASE_NotPresent will be returned.
	Notes:
*/

typedef struct ASIOClockSource
{
	long index;					// as used for ASIOSetClockSource()
	long associatedChannel;		// for instance, S/PDIF or AES/EBU
	long associatedGroup;		// see channel groups (ASIOGetChannelInfo())
	ASIOBool isCurrentSource;	// ASIOTrue if this is the current clock source
	char name[32];				// for user selection
} ASIOClockSource;

ASIOError ASIOGetClockSources(ASIOClockSource *clocks, long *numSources);
/* Purpose:
	  Get the available external audio clock sources
	Parameter:
	  clocks points to an array of ASIOClockSource structures:
	  	- index: this is used to identify the clock source
	  	  when ASIOSetClockSource() is accessed, should be
	  	  an index counting from zero
	  	- associatedInputChannel: the first channel of an associated
	  	  input group, if any.
	  	- associatedGroup: the group index of that channel.
	  	  groups of channels are defined to seperate for
	  	  instance analog, S/PDIF, AES/EBU, ADAT connectors etc,
	  	  when present simultaniously. Note that associated channel
	  	  is enumerated according to numInputs/numOutputs, means it
	  	  is independant from a group (see also ASIOGetChannelInfo())
	  	  inputs are associated to a clock if the physical connection
	  	  transfers both data and clock (like S/PDIF, AES/EBU, or
	  	  ADAT inputs). if there is no input channel associated with
	  	  the clock source (like Word Clock, or internal oscillator), both
	  	  associatedChannel and associatedGroup should be set to -1.
	  	- isCurrentSource: on exit, ASIOTrue if this is the current clock
	  	  source, ASIOFalse else
		- name: a null-terminated string for user selection of the available sources.
	  numSources:
	      on input: the number of allocated array members
	      on output: the number of available clock sources, at least
	      1 (internal clock generator).
	Returns:
	  If no input/output is present ASE_NotPresent will be returned.
	Notes:
*/

ASIOError ASIOSetClockSource(long index);
/* Purpose:
	  Set the audio clock source
	Parameter:
	  index as obtained from an inquiry to ASIOGetClockSources()
	Returns:
	  If no input/output is present ASE_NotPresent will be returned.
	  If the clock can not be selected because an input channel which
	  carries the current clock source is active, ASE_InvalidMode
	  *may* be returned (this depends on the properties of the driver
	  and/or hardware).
	Notes:
	  Should *not* return ASE_NoClock if there is no clock signal present
	  at the selected source; this will be inquired via ASIOGetSampleRate().
	  It should call the host callback procedure sampleRateHasChanged(),
	  if the switch causes a sample rate change, or if no external clock
	  is present at the selected source.
*/

ASIOError ASIOGetSamplePosition (ASIOSamples *sPos, ASIOTimeStamp *tStamp);
/* Purpose:
	  Inquires the sample position/time stamp pair.
	Parameter:
	  sPos will hold the sample position on return. The sample
	  position is reset to zero when ASIOStart() gets called.
	  tStamp will hold the system time when the sample position
	  was latched.
	Returns:
	  If no input/output is present, ASE_NotPresent will be returned.
	  If there is no clock, ASE_SPNotAdvancing will be returned.
	Notes:

	  in order to be able to synchronise properly,
	  the sample position / time stamp pair must refer to the current block,
	  that is, the engine will call ASIOGetSamplePosition() in its bufferSwitch()
	  callback and expect the time for the current block. thus, when requested
	  in the very first bufferSwitch after ASIO_Start(), the sample position
	  should be zero, and the time stamp should refer to the very time where
	  the stream was started. it also means that the sample position must be
	  block aligned. the driver must ensure proper interpolation if the system
	  time can not be determined for the block position. the driver is responsible
	  for precise time stamps as it usually has most direct access to lower
	  level resources. proper behaviour of ASIO_GetSamplePosition() and ASIO_GetLatencies()
	  are essential for precise media synchronization!
*/

typedef struct ASIOChannelInfo
{
	long channel;			// on input, channel index
	ASIOBool isInput;		// on input
	ASIOBool isActive;		// on exit
	long channelGroup;		// dto
	ASIOSampleType type;	// dto
	char name[32];			// dto
} ASIOChannelInfo;

ASIOError ASIOGetChannelInfo(ASIOChannelInfo *info);
/* Purpose:
	  retreive information about the nature of a channel
	Parameter:
	  info: pointer to a ASIOChannelInfo structure with
	  	- channel: on input, the channel index of the channel in question.
	  	- isInput: on input, ASIOTrue if info for an input channel is
	  	  requested, else output
		- channelGroup: on return, the channel group that the channel
		  belongs to. For drivers which support different types of
		  channels, like analog, S/PDIF, AES/EBU, ADAT etc interfaces,
		  there should be a reasonable grouping of these types. Groups
		  are always independant form a channel index, that is, a channel
		  index always counts from 0 to numInputs/numOutputs regardless
		  of the group it may belong to.
		  There will always be at least one group (group 0). Please
		  also note that by default, the host may decide to activate
		  channels 0 and 1; thus, these should belong to the most
		  useful type (analog i/o, if present).
	  	- type: on return, contains the sample type of the channel
	  	- isActive: on return, ASIOTrue if channel is active as it was
	  	  installed by ASIOCreateBuffers(), ASIOFalse else
	  	- name:  describing the type of channel in question. Used to allow
	  	  for user selection, and enabling of specific channels. examples:
	      "Analog In", "SPDIF Out" etc
	Returns:
	  If no input/output is present ASE_NotPresent will be returned.
	Notes:
	  If possible, the string should be organised such that the first
	  characters are most significantly describing the nature of the
	  port, to allow for identification even if the view showing the
	  port name is too small to display more than 8 characters, for
	  instance.
*/

//- - - - - - - - - - - - - - - - - - - - - - - - -
// Buffer preparation
//- - - - - - - - - - - - - - - - - - - - - - - - -

typedef struct ASIOBufferInfo
{
	ASIOBool isInput;			// on input:  ASIOTrue: input, else output
	long channelNum;			// on input:  channel index
	void *buffers[2];			// on output: double buffer addresses
} ASIOBufferInfo;

ASIOError ASIOCreateBuffers(ASIOBufferInfo *bufferInfos, long numChannels,
	long bufferSize, ASIOCallbacks *callbacks);

/* Purpose:
	  Allocates input/output buffers for all input and output channels to be activated.
	Parameter:
	  bufferInfos is a pointer to an array of ASIOBufferInfo structures:
	    - isInput: on input, ASIOTrue if the buffer is to be allocated
	      for an input, output buffer else
	    - channelNum: on input, the index of the channel in question
	      (counting from 0)
	    - buffers: on exit, 2 pointers to the halves of the channels' double-buffer.
	      the size of the buffer(s) of course depend on both the ASIOSampleType
	      as obtained from ASIOGetChannelInfo(), and bufferSize
	  numChannels is the sum of all input and output channels to be created;
	  thus bufferInfos is a pointer to an array of numChannels ASIOBufferInfo
	  structures.
	  bufferSize selects one of the possible buffer sizes as obtained from
	  ASIOGetBufferSizes().
	  callbacks is a pointer to an ASIOCallbacks structure.
	Returns:
	  If not enough memory is available ASE_NoMemory will be returned.
	  If no input/output is present ASE_NotPresent will be returned.
	  If bufferSize is not supported, or one or more of the bufferInfos elements
	  contain invalid settings, ASE_InvalidMode will be returned.
	Notes:
	  If individual channel selection is not possible but requested,
	  the driver has to handle this. namely, bufferSwitch() will only
	  have filled buffers of enabled outputs. If possible, processing
	  and buss activities overhead should be avoided for channels which
	  were not enabled here.
*/

ASIOError ASIODisposeBuffers(void);
/* Purpose:
	  Releases all buffers for the device.
	Parameter:
	  None.
	Returns:
	  If no buffer were ever prepared, ASE_InvalidMode will be returned.
	  If no input/output is present ASE_NotPresent will be returned.
	Notes:
	  This implies ASIOStop().
*/

ASIOError ASIOControlPanel(void);
/* Purpose:
	  request the driver to start a control panel component
	  for device specific user settings. This will not be
	  accessed on some platforms (where the component is accessed
	  instead).
	Parameter:
	  None.
	Returns:
	  If no panel is available ASE_NotPresent will be returned.
	  Actually, the return code is ignored.
	Notes:
	  if the user applied settings which require a re-configuration
	  of parts or all of the enigine and/or driver (such as a change of
	  the block size), the asioMessage callback can be used (see
	  ASIO_Callbacks).
*/

ASIOError ASIOFuture(long selector, void *params);
/* Purpose:
	  various
	Parameter:
	  selector: operation Code as to be defined. zero is reserved for
	  testing purposes.
	  params: depends on the selector; usually pointer to a structure
	  for passing and retreiving any type and amount of parameters.
	Returns:
	  the return value is also selector dependant. if the selector
	  is unknown, ASE_InvalidParameter should be returned to prevent
	  further calls with this selector. on success, ASE_SUCCESS
	  must be returned (note: ASE_OK is *not* sufficient!)
	Notes:
	  see selectors defined below.	  
*/

enum
{
	kAsioEnableTimeCodeRead = 1,	// no arguments
	kAsioDisableTimeCodeRead,		// no arguments
	kAsioSetInputMonitor,			// ASIOInputMonitor* in params
	kAsioTransport,					// ASIOTransportParameters* in params
	kAsioSetInputGain,				// ASIOChannelControls* in params, apply gain
	kAsioGetInputMeter,				// ASIOChannelControls* in params, fill meter
	kAsioSetOutputGain,				// ASIOChannelControls* in params, apply gain
	kAsioGetOutputMeter,			// ASIOChannelControls* in params, fill meter
	kAsioCanInputMonitor,			// no arguments for kAsioCanXXX selectors
	kAsioCanTimeInfo,
	kAsioCanTimeCode,
	kAsioCanTransport,
	kAsioCanInputGain,
	kAsioCanInputMeter,
	kAsioCanOutputGain,
	kAsioCanOutputMeter
};

typedef struct ASIOInputMonitor
{
	long input;		// this input was set to monitor (or off), -1: all
	long output;	// suggested output for monitoring the input (if so)
	long gain;		// suggested gain, ranging 0 - 0x7fffffffL (-inf to +12 dB)
	ASIOBool state;	// ASIOTrue => on, ASIOFalse => off
	long pan;		// suggested pan, 0 => all left, 0x7fffffff => right
} ASIOInputMonitor;

typedef struct ASIOChannelControls
{
	long channel;			// on input, channel index
	ASIOBool isInput;		// on input
	long gain;				// on input,  ranges 0 thru 0x7fffffff
	long meter;				// on return, ranges 0 thru 0x7fffffff
	char future[32];
} ASIOChannelControls;

typedef struct ASIOTransportParameters
{
	long command;		// see enum below
	ASIOSamples samplePosition;
	long track;
	long trackSwitches[16];		// 512 tracks on/off
	char future[64];
} ASIOTransportParameters;

enum
{
	kTransStart = 1,
	kTransStop,
	kTransLocate,		// to samplePosition
	kTransPunchIn,
	kTransPunchOut,
	kTransArmOn,		// track
	kTransArmOff,		// track
	kTransMonitorOn,	// track
	kTransMonitorOff,	// track
	kTransArm,			// trackSwitches
	kTransMonitor		// trackSwitches
};

ASIOError ASIOOutputReady(void);
/* Purpose:
	  this tells the driver that the host has completed processing
	  the output buffers. if the data format required by the hardware
	  differs from the supported asio formats, but the hardware
	  buffers are DMA buffers, the driver will have to convert
	  the audio stream data; as the bufferSwitch callback is
	  usually issued at dma block switch time, the driver will
	  have to convert the *previous* host buffer, which increases
	  the output latency by one block.
	  when the host finds out that ASIOOutputReady() returns
	  true, it will issue this call whenever it completed
	  output processing. then the driver can convert the
	  host data directly to the dma buffer to be played next,
	  reducing output latency by one block.
	  another way to look at it is, that the buffer switch is called
	  in order to pass the *input* stream to the host, so that it can
	  process the input into the output, and the output stream is passed
	  to the driver when the host has completed its process.
	Parameter:
		None
	Returns:
	  only if the above mentioned scenario is given, and a reduction
	  of output latency can be acheived by this mechanism, should
	  ASE_OK be returned. otherwise (and usually), ASE_NotPresent
	  should be returned in order to prevent further calls to this
	  function. note that the host may want to determine if it is
	  to use this when the system is not yet fully initialized, so
	  ASE_OK should always be returned if the mechanism makes sense.	  
	Notes:
	  please remeber to adjust ASIOGetLatencies() according to
	  whether ASIOOutputReady() was ever called or not, if your
	  driver supports this scenario.
	  also note that the engine may fail to call ASIO_OutputReady()
	  in time in overload cases. as already mentioned, bufferSwitch
      should be called for every block regardless of whether a block
      could be processed in time.
*/

// restore old alignment
#if defined(_MSC_VER) && !defined(__MWERKS__) 
#pragma pack(pop)
#elif PRAGMA_ALIGN_SUPPORTED
#pragma options align = reset
#endif

#endif


--- NEW FILE: asiosmpl.cpp ---
/*
	Steinberg Audio Stream I/O API
	(c) 1996, Steinberg Soft- und Hardware GmbH
	charlie (May 1996)

	asiosmpl.cpp
	
	sample implementation of asio. can be set to simulate input with some
	stupid oscillators.
	this driver doesn't output sound at all...
	timing is done via the extended time manager on the mac, and
	a simple thread on pc.
	you may test various configurations by changing the kNumInputs/Outputs,
	and kBlockFrames. note that when using wave generation, as i/o is not optimized
	at all, it moves quite a bit of memory, too many i/o channels may make it
	pretty slow.

	if you use this file as a template, make sure to resolve places where the
	search string !!! can be found...
*/

#include <stdio.h>
#include <string.h>
#include "asiosmpl.h"
//#include "virtape.h"

//------------------------------------------------------------------------------------------

// extern
void getNanoSeconds(ASIOTimeStamp *time);

// local

double AsioSamples2double (ASIOSamples* samples);

static const double twoRaisedTo32 = 4294967296.;
static const double twoRaisedTo32Reciprocal = 1. / twoRaisedTo32;

//------------------------------------------------------------------------------------------
// on windows, we do the COM stuff.

#if WINDOWS
#include "windows.h"
#include "mmsystem.h"

// class id. !!! NOTE: !!! you will obviously have to create your own class id!
// {188135E1-D565-11d2-854F-00A0C99F5D19}
CLSID IID_ASIO_DRIVER = { 0x188135e1, 0xd565, 0x11d2, { 0x85, 0x4f, 0x0, 0xa0, 0xc9, 0x9f, 0x5d, 0x19 } };

CFactoryTemplate g_Templates[1] = {
    {L"ASIOSAMPLE", &IID_ASIO_DRIVER, AsioSample::CreateInstance} 
};
int g_cTemplates = sizeof(g_Templates) / sizeof(g_Templates[0]);

CUnknown* AsioSample::CreateInstance (LPUNKNOWN pUnk, HRESULT *phr)
{
	return (CUnknown*)new AsioSample (pUnk,phr);
};

STDMETHODIMP AsioSample::NonDelegatingQueryInterface (REFIID riid, void ** ppv)
{
	if (riid == IID_ASIO_DRIVER)
	{
		return GetInterface (this, ppv);
	}
	return CUnknown::NonDelegatingQueryInterface (riid, ppv);
}

// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//		Register ASIO Driver
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
extern LONG RegisterAsioDriver (CLSID,char *,char *,char *,char *);
extern LONG UnregisterAsioDriver (CLSID,char *,char *);

//
// Server registration, called on REGSVR32.EXE "the dllname.dll"
//
HRESULT _stdcall DllRegisterServer()
{
	LONG	rc;
	char	errstr[128];

	rc = RegisterAsioDriver (IID_ASIO_DRIVER,"ASIOSample.dll","ASIO Sample Driver","ASIO Sample","Apartment");

	if (rc) {
		memset(errstr,0,128);
		sprintf(errstr,"Register Server failed ! (%d)",rc);
		MessageBox(0,(LPCTSTR)errstr,(LPCTSTR)"ASIO sample Driver",MB_OK);
		return -1;
	}

	return S_OK;
}

//
// Server unregistration
//
HRESULT _stdcall DllUnregisterServer()
{
	LONG	rc;
	char	errstr[128];

	rc = UnregisterAsioDriver (IID_ASIO_DRIVER,"ASIOSample.dll","ASIO Sample Driver");

	if (rc) {
		memset(errstr,0,128);
		sprintf(errstr,"Unregister Server failed ! (%d)",rc);
		MessageBox(0,(LPCTSTR)errstr,(LPCTSTR)"ASIO Korg1212 I/O Driver",MB_OK);
		return -1;
	}

	return S_OK;
}

//------------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------------
AsioSample::AsioSample (LPUNKNOWN pUnk, HRESULT *phr)
	: CUnknown("ASIOSAMPLE", pUnk, phr)

//------------------------------------------------------------------------------------------

#else

// when not on windows, we derive from AsioDriver
AsioSample::AsioSample () : AsioDriver ()

#endif
{
	long i;

	blockFrames = kBlockFrames;
	inputLatency = blockFrames;		// typically
	outputLatency = blockFrames * 2;
	// typically blockFrames * 2; try to get 1 by offering direct buffer
	// access, and using asioPostOutput for lower latency
	samplePosition = 0;
	sampleRate = 44100.;
	milliSeconds = (long)((double)(kBlockFrames * 1000) / sampleRate);
	active = false;
	started = false;
	timeInfoMode = false;
	tcRead = false;
	for (i = 0; i < kNumInputs; i++)
	{
		inputBuffers[i] = 0;
		inMap[i] = 0;
	}
#if TESTWAVES
	sawTooth = sineWave = 0;
#endif
	for (i = 0; i < kNumOutputs; i++)
	{
		outputBuffers[i] = 0;
		outMap[i] = 0;
	}
	callbacks = 0;
	activeInputs = activeOutputs = 0;
	toggle = 0;
}

//------------------------------------------------------------------------------------------
AsioSample::~AsioSample ()
{
	stop ();
	outputClose ();
	inputClose ();
	disposeBuffers ();
}

//------------------------------------------------------------------------------------------
void AsioSample::getDriverName (char *name)
{
	strcpy (name, "Sample ASIO");
}

//------------------------------------------------------------------------------------------
long AsioSample::getDriverVersion ()
{
	return 0x00000001L;
}

//------------------------------------------------------------------------------------------
void AsioSample::getErrorMessage (char *string)
{
	strcpy (string, errorMessage);
}

//------------------------------------------------------------------------------------------
ASIOBool AsioSample::init (void* sysRef)
{
	sysRef = sysRef;
	if (active)
		return true;
	strcpy (errorMessage, "ASIO Driver open Failure!");
	if (inputOpen ())
	{
		if (outputOpen ())
		{
			active = true;
			return true;
		}
	}
	timerOff ();		// de-activate 'hardware'

	outputClose ();
	inputClose ();
	return false;
}

//------------------------------------------------------------------------------------------
ASIOError AsioSample::start ()
{
	if (callbacks)
	{
		started = false;
		samplePosition = 0;
		theSystemTime.lo = theSystemTime.hi = 0;
		toggle = 0;

		timerOn ();			// activate 'hardware'
		started = true;

		return ASE_OK;
	}
	return ASE_NotPresent;
}

//------------------------------------------------------------------------------------------
ASIOError AsioSample::stop ()
{
	started = false;
	timerOff ();		// de-activate 'hardware'
	return ASE_OK;
}

//------------------------------------------------------------------------------------------
ASIOError AsioSample::getChannels (long *numInputChannels, long *numOutputChannels)
{
	*numInputChannels = kNumInputs;
	*numOutputChannels = kNumOutputs;
	return ASE_OK;
}

//------------------------------------------------------------------------------------------
ASIOError AsioSample::getLatencies (long *_inputLatency, long *_outputLatency)
{
	*_inputLatency = inputLatency;
	*_outputLatency = outputLatency;
	return ASE_OK;
}

//------------------------------------------------------------------------------------------
ASIOError AsioSample::getBufferSize (long *minSize, long *maxSize,
	long *preferredSize, long *granularity)
{
	*minSize = *maxSize = *preferredSize = blockFrames;		// allow this size only
	*granularity = 0;
	return ASE_OK;
}

//------------------------------------------------------------------------------------------
ASIOError AsioSample::canSampleRate (ASIOSampleRate sampleRate)
{
	if (sampleRate == 44100. || sampleRate == 48000.)		// allow these rates only
		return ASE_OK;
	return ASE_NoClock;
}

//------------------------------------------------------------------------------------------
ASIOError AsioSample::getSampleRate (ASIOSampleRate *sampleRate)
{
	*sampleRate = this->sampleRate;
	return ASE_OK;
}

//------------------------------------------------------------------------------------------
ASIOError AsioSample::setSampleRate (ASIOSampleRate sampleRate)
{
	if (sampleRate != 44100. && sampleRate != 48000.)
		return ASE_NoClock;
	if (sampleRate != this->sampleRate)
	{
		this->sampleRate = sampleRate;
		asioTime.timeInfo.sampleRate = sampleRate;
		asioTime.timeInfo.flags |= kSampleRateChanged;
		milliSeconds = (long)((double)(kBlockFrames * 1000) / this->sampleRate);
		if (callbacks && callbacks->sampleRateDidChange)
			callbacks->sampleRateDidChange (this->sampleRate);
	}
	return ASE_OK;
}

//------------------------------------------------------------------------------------------
ASIOError AsioSample::getClockSources (ASIOClockSource *clocks, long *numSources)
{
	// internal
	clocks->index = 0;
	clocks->associatedChannel = -1;
	clocks->associatedGroup = -1;
	clocks->isCurrentSource = ASIOTrue;
	strcpy(clocks->name, "Internal");
	*numSources = 1;
	return ASE_OK;
}

//------------------------------------------------------------------------------------------
ASIOError AsioSample::setClockSource (long index)
{
	if (!index)
	{
		asioTime.timeInfo.flags |= kClockSourceChanged;
		return ASE_OK;
	}
	return ASE_NotPresent;
}

//------------------------------------------------------------------------------------------
ASIOError AsioSample::getSamplePosition (ASIOSamples *sPos, ASIOTimeStamp *tStamp)
{
	tStamp->lo = theSystemTime.lo;
	tStamp->hi = theSystemTime.hi;
	if (samplePosition >= twoRaisedTo32)
	{
		sPos->hi = (unsigned long)(samplePosition * twoRaisedTo32Reciprocal);
		sPos->lo = (unsigned long)(samplePosition - (sPos->hi * twoRaisedTo32));
	}
	else
	{
		sPos->hi = 0;
		sPos->lo = (unsigned long)samplePosition;
	}
	return ASE_OK;
}

//------------------------------------------------------------------------------------------
ASIOError AsioSample::getChannelInfo (ASIOChannelInfo *info)
{
	if (info->channel < 0 || (info->isInput ? info->channel >= kNumInputs : info->channel >= kNumOutputs))
		return ASE_InvalidParameter;
#if WINDOWS
	info->type = ASIOSTInt16LSB;
#else
	info->type = ASIOSTInt16MSB;
#endif
	info->channelGroup = 0;
	info->isActive = ASIOFalse;
	long i;
	if (info->isInput)
	{
		for (i = 0; i < activeInputs; i++)
		{
			if (inMap[i] == info->channel)
			{
				info->isActive = ASIOTrue;
				break;
			}
		}
	}
	else
	{
		for (i = 0; i < activeOutputs; i++)
		{
			if (outMap[i] == info->channel)
			{
				info->isActive = ASIOTrue;
				break;
			}
		}
	}
	strcpy(info->name, "Sample ");
	return ASE_OK;
}

//------------------------------------------------------------------------------------------
ASIOError AsioSample::createBuffers (ASIOBufferInfo *bufferInfos, long numChannels,
	long bufferSize, ASIOCallbacks *callbacks)
{
	ASIOBufferInfo *info = bufferInfos;
	long i;
	bool notEnoughMem = false;

	activeInputs = 0;
	activeOutputs = 0;
	blockFrames = bufferSize;
	for (i = 0; i < numChannels; i++, info++)
	{
		if (info->isInput)
		{
			if (info->channelNum < 0 || info->channelNum >= kNumInputs)
				goto error;
			inMap[activeInputs] = info->channelNum;
			inputBuffers[activeInputs] = new short[blockFrames * 2];	// double buffer
			if (inputBuffers[activeInputs])
			{
				info->buffers[0] = inputBuffers[activeInputs];
				info->buffers[1] = inputBuffers[activeInputs] + blockFrames;
			}
			else
			{
				info->buffers[0] = info->buffers[1] = 0;
				notEnoughMem = true;
			}
			activeInputs++;
			if (activeInputs > kNumInputs)
			{
error:
				disposeBuffers();
				return ASE_InvalidParameter;
			}
		}
		else	// output			
		{
			if (info->channelNum < 0 || info->channelNum >= kNumOutputs)
				goto error;
			outMap[activeOutputs] = info->channelNum;
			outputBuffers[activeOutputs] = new short[blockFrames * 2];	// double buffer
			if (outputBuffers[activeOutputs])
			{
				info->buffers[0] = outputBuffers[activeOutputs];
				info->buffers[1] = outputBuffers[activeOutputs] + blockFrames;
			}
			else
			{
				info->buffers[0] = info->buffers[1] = 0;
				notEnoughMem = true;
			}
			activeOutputs++;
			if (activeOutputs > kNumOutputs)
			{
				activeOutputs--;
				disposeBuffers();
				return ASE_InvalidParameter;
			}
		}
	}		
	if (notEnoughMem)
	{
		disposeBuffers();
		return ASE_NoMemory;
	}

	this->callbacks = callbacks;
	if (callbacks->asioMessage (kAsioSupportsTimeInfo, 0, 0, 0))
	{
		timeInfoMode = true;
		asioTime.timeInfo.speed = 1.;
		asioTime.timeInfo.systemTime.hi = asioTime.timeInfo.systemTime.lo = 0;
		asioTime.timeInfo.samplePosition.hi = asioTime.timeInfo.samplePosition.lo = 0;
		asioTime.timeInfo.sampleRate = sampleRate;
		asioTime.timeInfo.flags = kSystemTimeValid | kSamplePositionValid | kSampleRateValid;

		asioTime.timeCode.speed = 1.;
		asioTime.timeCode.timeCodeSamples.lo = asioTime.timeCode.timeCodeSamples.hi = 0;
		asioTime.timeCode.flags = kTcValid | kTcRunning ;
	}
	else
		timeInfoMode = false;	
	return ASE_OK;
}

//---------------------------------------------------------------------------------------------
ASIOError AsioSample::disposeBuffers()
{
	long i;
	
	callbacks = 0;
	stop();
	for (i = 0; i < activeInputs; i++)
		delete inputBuffers[i];
	activeInputs = 0;
	for (i = 0; i < activeOutputs; i++)
		delete outputBuffers[i];
	activeOutputs = 0;
	return ASE_OK;
}

//---------------------------------------------------------------------------------------------
ASIOError AsioSample::controlPanel()
{
	return ASE_NotPresent;
}

//---------------------------------------------------------------------------------------------
ASIOError AsioSample::future (long selector, void* opt)	// !!! check properties 
{
	ASIOTransportParameters* tp = (ASIOTransportParameters*)opt;
	switch (selector)
	{
		case kAsioEnableTimeCodeRead:	tcRead = true;	return ASE_SUCCESS;
		case kAsioDisableTimeCodeRead:	tcRead = false;	return ASE_SUCCESS;
		case kAsioSetInputMonitor:		return ASE_SUCCESS;	// for testing!!!
		case kAsioCanInputMonitor:		return ASE_SUCCESS;	// for testing!!!
		case kAsioCanTimeInfo:			return ASE_SUCCESS;
		case kAsioCanTimeCode:			return ASE_SUCCESS;
	}
	return ASE_NotPresent;
}

//--------------------------------------------------------------------------------------------------------
// private methods
//--------------------------------------------------------------------------------------------------------

//--------------------------------------------------------------------------------------------------------
// input
//--------------------------------------------------------------------------------------------------------

//---------------------------------------------------------------------------------------------
bool AsioSample::inputOpen ()
{
#if TESTWAVES
	sineWave = new short[blockFrames];
	if (!sineWave)
	{
		strcpy (errorMessage, "ASIO Sample Driver: Out of Memory!");
		return false;
	}
	makeSine (sineWave);

	sawTooth = new short[blockFrames];
	if (!sawTooth)
	{
		strcpy(errorMessage, "ASIO Sample Driver: Out of Memory!");
		return false;
	}
	makeSaw(sawTooth);
#endif
	return true;
}

#if TESTWAVES

#include <math.h>

const double pi = 0.3141592654;

//---------------------------------------------------------------------------------------------
void AsioSample::makeSine (short *wave)
{
	double frames = (double)blockFrames;
	double i, f = (pi * 2.) / frames;

	for (i = 0; i < frames; i++)
		*wave++ = (short)((double)0x7fff * sin(f * i));
}

//---------------------------------------------------------------------------------------------
void AsioSample::makeSaw(short *wave)
{
	double frames = (double)blockFrames;
	double i, f = 2. / frames;

	for (i = 0; i < frames; i++)
		*wave++ = (short)((double)0x7fff * (-1. + f * i));
}
#endif

//---------------------------------------------------------------------------------------------
void AsioSample::inputClose ()
{
#if TESTWAVES
	if (sineWave)
		delete sineWave;
	sineWave = 0;
	if (sawTooth)
		delete sawTooth;
	sawTooth = 0;
#endif
}

//---------------------------------------------------------------------------------------------
void AsioSample::input()
{
#if TESTWAVES
	long i;
	short *in = 0;

	for (i = 0; i < activeInputs; i++)
	{
		in = inputBuffers[i];
		if (in)
		{
			if (toggle)
				in += blockFrames;
			if ((i & 1) && sawTooth)
				memcpy(in, sawTooth, (unsigned long)(blockFrames * 2));
			else if (sineWave)
				memcpy(in, sineWave, (unsigned long)(blockFrames * 2));
		}
	}
#endif
}

//------------------------------------------------------------------------------------------------------------------
// output
//------------------------------------------------------------------------------------------------------------------

//---------------------------------------------------------------------------------------------
bool AsioSample::outputOpen()
{
	return true;
}

//---------------------------------------------------------------------------------------------
void AsioSample::outputClose ()
{
}

//---------------------------------------------------------------------------------------------
void AsioSample::output ()
{
}

//---------------------------------------------------------------------------------------------
void AsioSample::bufferSwitch ()
{
	if (started && callbacks)
	{
		getNanoSeconds(&theSystemTime);			// latch system time
		input();
		output();
		samplePosition += blockFrames;
		if (timeInfoMode)
			bufferSwitchX ();
		else
			callbacks->bufferSwitch (toggle, ASIOFalse);
		toggle = toggle ? 0 : 1;
	}
}

//---------------------------------------------------------------------------------------------
// asio2 buffer switch
void AsioSample::bufferSwitchX ()
{
	getSamplePosition (&asioTime.timeInfo.samplePosition, &asioTime.timeInfo.systemTime);
	long offset = toggle ? blockFrames : 0;
	if (tcRead)
	{	// Create a fake time code, which is 10 minutes ahead of the card's sample position
		// Please note that for simplicity here time code will wrap after 32 bit are reached
		asioTime.timeCode.timeCodeSamples.lo = asioTime.timeInfo.samplePosition.lo + 600.0 * sampleRate;
		asioTime.timeCode.timeCodeSamples.hi = 0;
	}
	callbacks->bufferSwitchTimeInfo (&asioTime, toggle, ASIOFalse);
	asioTime.timeInfo.flags &= ~(kSampleRateChanged | kClockSourceChanged);
}

//---------------------------------------------------------------------------------------------
ASIOError AsioSample::outputReady ()
{
	return ASE_NotPresent;
}

//---------------------------------------------------------------------------------------------
double AsioSamples2double (ASIOSamples* samples)
{
	double a = (double)(samples->lo);
	if (samples->hi)
		a += (double)(samples->hi) * twoRaisedTo32;
	return a;
}


--- NEW FILE: combase.cpp ---
//==========================================================================;
//
//  THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
//  KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
//  IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
//  PURPOSE.
//
//  Copyright (c) 1992 - 1996  Microsoft Corporation.  All Rights Reserved.
//
//--------------------------------------------------------------------------;

// Base class hierachy for creating COM objects, December 1994

#include <windows.h>
#include "wxdebug.h"
#include "combase.h"
#pragma warning( disable : 4514 )   // Disable warnings re unused inline functions


/* Define the static member variable */

LONG CBaseObject::m_cObjects = 0;


/* Constructor */

CBaseObject::CBaseObject(TCHAR *pName)
{
    /* Increment the number of active objects */
    InterlockedIncrement(&m_cObjects);

#ifdef DEBUG
    m_dwCookie = DbgRegisterObjectCreation(pName);
#endif
}


/* Destructor */

CBaseObject::~CBaseObject()
{
    /* Decrement the number of objects active */
    InterlockedDecrement(&m_cObjects);

#ifdef DEBUG
    DbgRegisterObjectDestruction(m_dwCookie);
#endif
}


/* Constructor */

// We know we use "this" in the initialization list, we also know we don't modify *phr.
#pragma warning( disable : 4355 4100 ) 
CUnknown::CUnknown(TCHAR *pName, LPUNKNOWN pUnk, HRESULT *phr) 
: CBaseObject(pName)
/* Start the object with a reference count of zero - when the      */
/* object is queried for it's first interface this may be          */
/* incremented depending on whether or not this object is          */
/* currently being aggregated upon                                 */
, m_cRef(0)
/* Set our pointer to our IUnknown interface.                      */
/* If we have an outer, use its, otherwise use ours.               */
/* This pointer effectivly points to the owner of                  */
/* this object and can be accessed by the GetOwner() method.       */
, m_pUnknown( pUnk != 0 ? pUnk : reinterpret_cast<LPUNKNOWN>( static_cast<PNDUNKNOWN>(this) ) )
 /* Why the double cast?  Well, the inner cast is a type-safe cast */
 /* to pointer to a type from which we inherit.  The second is     */
 /* type-unsafe but works because INonDelegatingUnknown "behaves   */
 /* like" IUnknown. (Only the names on the methods change.)        */
{
    // Everything we need to do has been done in the initializer list
}
#pragma warning( default : 4355 4100 ) 

/* QueryInterface */

STDMETHODIMP CUnknown::NonDelegatingQueryInterface(REFIID riid, void ** ppv)
{
    CheckPointer(ppv,E_POINTER);
    ValidateReadWritePtr(ppv,sizeof(PVOID));

    /* We know only about IUnknown */

    if (riid == IID_IUnknown) {
        GetInterface((LPUNKNOWN) (PNDUNKNOWN) this, ppv);
        return NOERROR;
    } else {
        *ppv = NULL;
        return E_NOINTERFACE;
    }
}

/* We have to ensure that we DON'T use a max macro, since these will typically   */
/* lead to one of the parameters being evaluated twice.  Since we are worried    */
/* about concurrency, we can't afford to access the m_cRef twice since we can't  */
/* afford to run the risk that its value having changed between accesses.        */
#ifdef max
    #undef max
#endif

template<class T> inline static T max( const T & a, const T & b )
{
    return a > b ? a : b;
}

/* AddRef */

STDMETHODIMP_(ULONG) CUnknown::NonDelegatingAddRef()
{
    LONG lRef = InterlockedIncrement( &m_cRef );
    ASSERT(lRef > 0);
    DbgLog((LOG_MEMORY,3,TEXT("    Obj %d ref++ = %d"),
           m_dwCookie, m_cRef));
    return max(ULONG(m_cRef), 1ul);
}



/* Release */

STDMETHODIMP_(ULONG) CUnknown::NonDelegatingRelease()
{
    /* If the reference count drops to zero delete ourselves */

    LONG lRef = InterlockedDecrement( &m_cRef );
    ASSERT(lRef >= 0);

    DbgLog((LOG_MEMORY,3,TEXT("    Object %d ref-- = %d"),
	    m_dwCookie, m_cRef));
    if (lRef == 0) {

        // COM rules say we must protect against re-entrancy.
        // If we are an aggregator and we hold our own interfaces
        // on the aggregatee, the QI for these interfaces will
        // addref ourselves. So after doing the QI we must release
        // a ref count on ourselves. Then, before releasing the
        // private interface, we must addref ourselves. When we do
        // this from the destructor here it will result in the ref
        // count going to 1 and then back to 0 causing us to
        // re-enter the destructor. Hence we add an extra refcount here
        // once we know we will delete the object.
        // for an example aggregator see filgraph\distrib.cpp.

        m_cRef++;

        delete this;
        return ULONG(0);
    } else {
        return max(ULONG(m_cRef), 1ul);
    }
}


/* Return an interface pointer to a requesting client
   performing a thread safe AddRef as necessary */

HRESULT CUnknown::GetInterface(LPUNKNOWN pUnk, void **ppv)
{
    CheckPointer(ppv, E_POINTER);
    *ppv = pUnk;
    pUnk->AddRef();
    return NOERROR;
}


/* Compares two interfaces and returns TRUE if they are on the same object */

BOOL IsEqualObject(IUnknown *pFirst, IUnknown *pSecond)
{
    /*  Different objects can't have the same interface pointer for
        any interface
    */
    if (pFirst == pSecond) {
        return TRUE;
    }
    /*  OK - do it the hard way - check if they have the same
        IUnknown pointers - a single object can only have one of these
    */
    LPUNKNOWN pUnknown1;     // Retrieve the IUnknown interface
    LPUNKNOWN pUnknown2;     // Retrieve the other IUnknown interface
    HRESULT hr;              // General OLE return code

    ASSERT(pFirst);
    ASSERT(pSecond);

    /* See if the IUnknown pointers match */

    hr = pFirst->QueryInterface(IID_IUnknown,(void **) &pUnknown1);
    ASSERT(SUCCEEDED(hr));
    ASSERT(pUnknown1);

    hr = pSecond->QueryInterface(IID_IUnknown,(void **) &pUnknown2);
    ASSERT(SUCCEEDED(hr));
    ASSERT(pUnknown2);

    /* Release the extra interfaces we hold */

    pUnknown1->Release();
    pUnknown2->Release();
    return (pUnknown1 == pUnknown2);
}

--- NEW FILE: asiodrivers.cpp ---
#include <string.h>
#include "asiodrivers.h"

AsioDrivers* asioDrivers = 0;

bool loadAsioDriver(char *name);

bool loadAsioDriver(char *name)
{
	if(!asioDrivers)
		asioDrivers = new AsioDrivers();
	if(asioDrivers)
		return asioDrivers->loadDriver(name);
	return false;
}

//------------------------------------------------------------------------------------

#if MAC

bool resolveASIO(unsigned long aconnID);

AsioDrivers::AsioDrivers() : CodeFragments("ASIO Drivers", 'AsDr', 'Asio')
{
	connID = -1;
	curIndex = -1;
}

AsioDrivers::~AsioDrivers()
{
	removeCurrentDriver();
}

bool AsioDrivers::getCurrentDriverName(char *name)
{
	if(curIndex >= 0)
		return getName(curIndex, name);
	return false;
}

long AsioDrivers::getDriverNames(char **names, long maxDrivers)
{
	for(long i = 0; i < getNumFragments() && i < maxDrivers; i++)
		getName(i, names[i]);
	return getNumFragments() < maxDrivers ? getNumFragments() : maxDrivers;
}

bool AsioDrivers::loadDriver(char *name)
{
	char dname[64];
	unsigned long newID;

	for(long i = 0; i < getNumFragments(); i++)
	{
		if(getName(i, dname) && !strcmp(name, dname))
		{
			if(newInstance(i, &newID))
			{
				if(resolveASIO(newID))
				{
					if(connID != -1)
						removeInstance(curIndex, connID);
					curIndex = i;
					connID = newID;
					return true;
				}
			}
			break;
		}
	}
	return false;
}

void AsioDrivers::removeCurrentDriver()
{
	if(connID != -1)
		removeInstance(curIndex, connID);
	connID = -1;
	curIndex = -1;
}

//------------------------------------------------------------------------------------

#elif WINDOWS

#include "iasiodrv.h"

extern IASIO* theAsioDriver;

AsioDrivers::AsioDrivers() : AsioDriverList()
{
	curIndex = -1;
}

AsioDrivers::~AsioDrivers()
{
}

bool AsioDrivers::getCurrentDriverName(char *name)
{
	if(curIndex >= 0)
		return asioGetDriverName(curIndex, name, 32) == 0 ? true : false;
	name[0] = 0;
	return false;
}

long AsioDrivers::getDriverNames(char **names, long maxDrivers)
{
	for(long i = 0; i < asioGetNumDev() && i < maxDrivers; i++)
		asioGetDriverName(i, names[i], 32);
	return asioGetNumDev() < maxDrivers ? asioGetNumDev() : maxDrivers;
}

bool AsioDrivers::loadDriver(char *name)
{
	char dname[64];
	char curName[64];

	for(long i = 0; i < asioGetNumDev(); i++)
	{
		if(!asioGetDriverName(i, dname, 32) && !strcmp(name, dname))
		{
			curName[0] = 0;
			getCurrentDriverName(curName);	// in case we fail...
			removeCurrentDriver();

			if(!asioOpenDriver(i, (void **)&theAsioDriver))
			{
				curIndex = i;
				return true;
			}
			else
			{
				theAsioDriver = 0;
				if(curName[0] && strcmp(dname, curName))
					loadDriver(curName);	// try restore
			}
			break;
		}
	}
	return false;
}

void AsioDrivers::removeCurrentDriver()
{
	if(curIndex != -1)
		asioCloseDriver(curIndex);
	curIndex = -1;
}

#elif SGI || BEOS

#include "asiolist.h"

AsioDrivers::AsioDrivers() 
	: AsioDriverList()
{
	curIndex = -1;
}

AsioDrivers::~AsioDrivers()
{
}

bool AsioDrivers::getCurrentDriverName(char *name)
{
	return false;
}

long AsioDrivers::getDriverNames(char **names, long maxDrivers)
{
	return 0;
}

bool AsioDrivers::loadDriver(char *name)
{
	return false;
}

void AsioDrivers::removeCurrentDriver()
{
}

#else
#error implement me
#endif

--- NEW FILE: iasiothiscallresolver.h ---
// ****************************************************************************
// File:			IASIOThiscallResolver.h
// Description:     The IASIOThiscallResolver class implements the IASIO
//					interface and acts as a proxy to the real IASIO interface by
//                  calling through its vptr table using the thiscall calling
//                  convention. To put it another way, we interpose
//                  IASIOThiscallResolver between ASIO SDK code and the driver.
//                  This is necessary because most non-Microsoft compilers don't
//                  implement the thiscall calling convention used by IASIO.
//
//					iasiothiscallresolver.cpp contains the background of this
//					problem plus a technical description of the vptr
//                  manipulations.
//
//					In order to use this mechanism one simply has to add
//					iasiothiscallresolver.cpp to the list of files to compile
//                  and #include <iasiothiscallresolver.h>
//
//					Note that this #include must come after the other ASIO SDK
//                  #includes, for example:
//
//					#include <windows.h>
//					#include <asiosys.h>
//					#include <asio.h>
//					#include <asiodrivers.h>
//					#include <iasiothiscallresolver.h>
//
//					Actually the important thing is to #include
//                  <iasiothiscallresolver.h> after <asio.h>. We have
//                  incorporated a test to enforce this ordering.
//
//					The code transparently takes care of the interposition by
//                  using macro substitution to intercept calls to ASIOInit()
//                  and ASIOExit(). We save the original ASIO global
//                  "theAsioDriver" in our "that" variable, and then set
//                  "theAsioDriver" to equal our IASIOThiscallResolver instance.
//
// 					Whilst this method of resolving the thiscall problem requires
//					the addition of #include <iasiothiscallresolver.h> to client
//                  code it has the advantage that it does not break the terms
//                  of the ASIO licence by publishing it. We are NOT modifying
//                  any Steinberg code here, we are merely implementing the IASIO
//					interface in the same way that we would need to do if we
//					wished to provide an open source ASIO driver.
//
//					For compilation with MinGW -lole32 needs to be added to the
//                  linker options. For BORLAND, linking with Import32.lib is
//                  sufficient.
//
//					The dependencies are with: CoInitialize, CoUninitialize,
//					CoCreateInstance, CLSIDFromString - used by asiolist.cpp
//					and are required on Windows whether ThiscallResolver is used
//					or not.
//
//					Searching for the above strings in the root library path
//					of your compiler should enable the correct libraries to be
//					identified if they aren't immediately obvious.
//
//                  Note that the current implementation of IASIOThiscallResolver
//                  is not COM compliant - it does not correctly implement the
//                  IUnknown interface. Implementing it is not necessary because
//                  it is not called by parts of the ASIO SDK which call through
//                  theAsioDriver ptr. The IUnknown methods are implemented as
//                  assert(false) to ensure that the code fails if they are
//                  ever called.
// Restrictions:	None. Public Domain & Open Source distribute freely
//					You may use IASIOThiscallResolver commercially as well as
//                  privately.
//					You the user assume the responsibility for the use of the
//					files, binary or text, and there is no guarantee or warranty,
//					expressed or implied, including but not limited to the
//					implied warranties of merchantability and fitness for a
//					particular purpose. You assume all responsibility and agree
//					to hold no entity, copyright holder or distributors liable
//					for any loss of data or inaccurate representations of data
//					as a result of using IASIOThiscallResolver.
// Version:         1.4 Added separate macro CALL_THISCALL_1_DOUBLE from
//                  Andrew Baldwin, and volatile for whole gcc asm blocks,
//                  both for compatibility with newer gcc versions. Cleaned up
//                  Borland asm to use one less register.
//                  1.3 Switched to including assert.h for better compatibility.
//                  Wrapped entire .h and .cpp contents with a check for
//                  _MSC_VER to provide better compatibility with MS compilers.
//                  Changed Singleton implementation to use static instance
//                  instead of freestore allocated instance. Removed ASIOExit
//                  macro as it is no longer needed.
//                  1.2 Removed semicolons from ASIOInit and ASIOExit macros to
//                  allow them to be embedded in expressions (if statements).
//                  Cleaned up some comments. Removed combase.c dependency (it
//                  doesn't compile with BCB anyway) by stubbing IUnknown.
//                  1.1 Incorporated comments from Ross Bencina including things
//					such as changing name from ThiscallResolver to
//					IASIOThiscallResolver, tidying up the constructor, fixing
//					a bug in IASIOThiscallResolver::ASIOExit() and improving
//					portability through the use of conditional compilation
//					1.0 Initial working version.
// Created:			6/09/2003
// Authors:         Fraser Adams
//                  Ross Bencina
//                  Rene G. Ceballos
//                  Martin Fay
//                  Antti Silvast
//                  Andrew Baldwin
//
// ****************************************************************************


#ifndef included_iasiothiscallresolver_h
#define included_iasiothiscallresolver_h

// We only need IASIOThiscallResolver at all if we are on Win32. For other
// platforms we simply bypass the IASIOThiscallResolver definition to allow us
// to be safely #include'd whatever the platform to keep client code portable
#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__)


// If microsoft compiler we can call IASIO directly so IASIOThiscallResolver
// is not used.
#if !defined(_MSC_VER)


// The following is in order to ensure that this header is only included after
// the other ASIO headers (except for the case of iasiothiscallresolver.cpp).
// We need to do this because IASIOThiscallResolver works by eclipsing the
// original definition of ASIOInit() with a macro (see below).
#if !defined(iasiothiscallresolver_sourcefile)
	#if !defined(__ASIO_H)
	#error iasiothiscallresolver.h must be included AFTER asio.h
	#endif
#endif

#include <windows.h>
#include <asiodrvr.h> /* From ASIO SDK */


class IASIOThiscallResolver : public IASIO {
private:
	IASIO* that_; // Points to the real IASIO

	static IASIOThiscallResolver instance; // Singleton instance

	// Constructors - declared private so construction is limited to
    // our Singleton instance
    IASIOThiscallResolver();
	IASIOThiscallResolver(IASIO* that);
public:

    // Methods from the IUnknown interface. We don't fully implement IUnknown
    // because the ASIO SDK never calls these methods through theAsioDriver ptr.
    // These methods are implemented as assert(false).
    virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppv);
    virtual ULONG STDMETHODCALLTYPE AddRef();
    virtual ULONG STDMETHODCALLTYPE Release();

    // Methods from the IASIO interface, implemented as forwarning calls to that.
	virtual ASIOBool init(void *sysHandle);
	virtual void getDriverName(char *name);
	virtual long getDriverVersion();
	virtual void getErrorMessage(char *string);
	virtual ASIOError start();
	virtual ASIOError stop();
	virtual ASIOError getChannels(long *numInputChannels, long *numOutputChannels);
	virtual ASIOError getLatencies(long *inputLatency, long *outputLatency);
	virtual ASIOError getBufferSize(long *minSize, long *maxSize, long *preferredSize, long *granularity);
	virtual ASIOError canSampleRate(ASIOSampleRate sampleRate);
	virtual ASIOError getSampleRate(ASIOSampleRate *sampleRate);
	virtual ASIOError setSampleRate(ASIOSampleRate sampleRate);
	virtual ASIOError getClockSources(ASIOClockSource *clocks, long *numSources);
	virtual ASIOError setClockSource(long reference);
	virtual ASIOError getSamplePosition(ASIOSamples *sPos, ASIOTimeStamp *tStamp);
	virtual ASIOError getChannelInfo(ASIOChannelInfo *info);
	virtual ASIOError createBuffers(ASIOBufferInfo *bufferInfos, long numChannels, long bufferSize, ASIOCallbacks *callbacks);
	virtual ASIOError disposeBuffers();
	virtual ASIOError controlPanel();
	virtual ASIOError future(long selector,void *opt);
	virtual ASIOError outputReady();

    // Class method, see ASIOInit() macro below.
    static ASIOError ASIOInit(ASIODriverInfo *info); // Delegates to ::ASIOInit
};


// Replace calls to ASIOInit with our interposing version.
// This macro enables us to perform thiscall resolution simply by #including
// <iasiothiscallresolver.h> after the asio #includes (this file _must_ be
// included _after_ the asio #includes)

#define ASIOInit(name) IASIOThiscallResolver::ASIOInit((name))


#endif /* !defined(_MSC_VER) */

#endif /* Win32 */

#endif /* included_iasiothiscallresolver_h */



--- NEW FILE: iasiodrv.h ---
#include "asiosys.h"
#include "asio.h"

/* Forward Declarations */ 

#ifndef __ASIODRIVER_FWD_DEFINED__
#define __ASIODRIVER_FWD_DEFINED__
typedef interface IASIO IASIO;
#endif 	/* __ASIODRIVER_FWD_DEFINED__ */

interface IASIO : public IUnknown
{

	virtual ASIOBool init(void *sysHandle) = 0;
	virtual void getDriverName(char *name) = 0;	
	virtual long getDriverVersion() = 0;
	virtual void getErrorMessage(char *string) = 0;	
	virtual ASIOError start() = 0;
	virtual ASIOError stop() = 0;
	virtual ASIOError getChannels(long *numInputChannels, long *numOutputChannels) = 0;
	virtual ASIOError getLatencies(long *inputLatency, long *outputLatency) = 0;
	virtual ASIOError getBufferSize(long *minSize, long *maxSize,
		long *preferredSize, long *granularity) = 0;
	virtual ASIOError canSampleRate(ASIOSampleRate sampleRate) = 0;
	virtual ASIOError getSampleRate(ASIOSampleRate *sampleRate) = 0;
	virtual ASIOError setSampleRate(ASIOSampleRate sampleRate) = 0;
	virtual ASIOError getClockSources(ASIOClockSource *clocks, long *numSources) = 0;
	virtual ASIOError setClockSource(long reference) = 0;
	virtual ASIOError getSamplePosition(ASIOSamples *sPos, ASIOTimeStamp *tStamp) = 0;
	virtual ASIOError getChannelInfo(ASIOChannelInfo *info) = 0;
	virtual ASIOError createBuffers(ASIOBufferInfo *bufferInfos, long numChannels,
		long bufferSize, ASIOCallbacks *callbacks) = 0;
	virtual ASIOError disposeBuffers() = 0;
	virtual ASIOError controlPanel() = 0;
	virtual ASIOError future(long selector,void *opt) = 0;
	virtual ASIOError outputReady() = 0;
};

--- NEW FILE: asiolist.h ---
#ifndef __asiolist__
#define __asiolist__

#define DRVERR			-5000
#define DRVERR_INVALID_PARAM		DRVERR-1
#define DRVERR_DEVICE_ALREADY_OPEN	DRVERR-2
#define DRVERR_DEVICE_NOT_FOUND		DRVERR-3

#define MAXPATHLEN			512
#define MAXDRVNAMELEN		128

struct asiodrvstruct
{
	int						drvID;
	CLSID					clsid;
	char					dllpath[MAXPATHLEN];
	char					drvname[MAXDRVNAMELEN];
	LPVOID					asiodrv;
	struct asiodrvstruct	*next;
};

typedef struct asiodrvstruct ASIODRVSTRUCT;
typedef ASIODRVSTRUCT	*LPASIODRVSTRUCT;

class AsioDriverList {
public:
	AsioDriverList();
	~AsioDriverList();
	
	LONG asioOpenDriver (int,VOID **);
	LONG asioCloseDriver (int);

	// nice to have
	LONG asioGetNumDev (VOID);
	LONG asioGetDriverName (int,char *,int);		
	LONG asioGetDriverPath (int,char *,int);
	LONG asioGetDriverCLSID (int,CLSID *);

	// or use directly access
	LPASIODRVSTRUCT	lpdrvlist;
	int				numdrv;
};

typedef class AsioDriverList *LPASIODRIVERLIST;

#endif

Index: pa_asio.cpp
===================================================================
RCS file: /cvsroot/pure-data/pd/portaudio/pa_asio/pa_asio.cpp,v
retrieving revision 1.1.1.2.2.2
retrieving revision 1.1.1.2.2.3
diff -C2 -d -r1.1.1.2.2.2 -r1.1.1.2.2.3
*** pa_asio.cpp	13 Sep 2003 09:03:07 -0000	1.1.1.2.2.2
--- pa_asio.cpp	13 Sep 2004 13:22:53 -0000	1.1.1.2.2.3
***************
*** 86,90 ****
  #include "asio.h"
  #include "asiodrivers.h"
! 
  
  #if MAC
--- 86,90 ----
  #include "asio.h"
  #include "asiodrivers.h"
! #include "iasiothiscallresolver.h"
  
  #if MAC

--- NEW FILE: dllentry.cpp ---
//==========================================================================;
//
//  THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
//  KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
//  IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
//  PURPOSE.
//
//  Copyright (c) 1992 - 1996  Microsoft Corporation.  All Rights Reserved.
//
//--------------------------------------------------------------------------;

//
// classes used to support dll entrypoints for COM objects.
//
// #include "switches.h"

#include <windows.h>
#include "wxdebug.h"
#include "combase.h"
#ifdef DEBUG
#include <tchar.h>
#endif

#include <stdio.h>

extern CFactoryTemplate g_Templates[];
extern int g_cTemplates;

HINSTANCE hinstance = 0;
DWORD	  g_amPlatform;		// VER_PLATFORM_WIN32_WINDOWS etc... (from GetVersionEx)
OSVERSIONINFO g_osInfo;

//
// an instance of this is created by the DLLGetClassObject entrypoint
// it uses the CFactoryTemplate object it is given to support the
// IClassFactory interface

class CClassFactory : public IClassFactory
{

private:
    const CFactoryTemplate * m_pTemplate;

    ULONG m_cRef;

    static int m_cLocked;
public:
    CClassFactory(const CFactoryTemplate *);

    // IUnknown
    STDMETHODIMP QueryInterface(REFIID riid, void ** ppv);
    STDMETHODIMP_(ULONG)AddRef();
    STDMETHODIMP_(ULONG)Release();

    // IClassFactory
    STDMETHODIMP CreateInstance(LPUNKNOWN pUnkOuter, REFIID riid, void **pv);
    STDMETHODIMP LockServer(BOOL fLock);

    // allow DLLGetClassObject to know about global server lock status
    static BOOL IsLocked() {
        return (m_cLocked > 0);
    };
};

// process-wide dll locked state
int CClassFactory::m_cLocked = 0;

CClassFactory::CClassFactory(const CFactoryTemplate *pTemplate)
{
    m_cRef = 0;
    m_pTemplate = pTemplate;
}


STDMETHODIMP CClassFactory::QueryInterface(REFIID riid,void **ppv)
{
    CheckPointer(ppv,E_POINTER)
    ValidateReadWritePtr(ppv,sizeof(PVOID));
    *ppv = NULL;

    // any interface on this object is the object pointer.
    if ((riid == IID_IUnknown) || (riid == IID_IClassFactory)) {
        *ppv = (LPVOID) this;
	// AddRef returned interface pointer
        ((LPUNKNOWN) *ppv)->AddRef();
        return NOERROR;
    }

    return ResultFromScode(E_NOINTERFACE);
}


STDMETHODIMP_(ULONG) CClassFactory::AddRef()
{
    return ++m_cRef;
}

STDMETHODIMP_(ULONG) CClassFactory::Release()
{
	LONG	rc;

    if (--m_cRef == 0) {
		delete this;
		rc = 0;
    } else rc = m_cRef;

	return rc;
}

STDMETHODIMP CClassFactory::CreateInstance(LPUNKNOWN pUnkOuter,REFIID riid,void **pv)
{
	CheckPointer(pv,E_POINTER)
    ValidateReadWritePtr(pv,sizeof(void *));

    /* Enforce the normal OLE rules regarding interfaces and delegation */

    if (pUnkOuter != NULL) {
        if (IsEqualIID(riid,IID_IUnknown) == FALSE) {
            return ResultFromScode(E_NOINTERFACE);
        }
    }

    /* Create the new object through the derived class's create function */

    HRESULT hr = NOERROR;
    CUnknown *pObj = m_pTemplate->CreateInstance(pUnkOuter, &hr);

    if (pObj == NULL) {
        return E_OUTOFMEMORY;
    }

    /* Delete the object if we got a construction error */

	if (FAILED(hr)) {
		delete pObj;
      return hr;
   }

    /* Get a reference counted interface on the object */

    /* We wrap the non-delegating QI with NDAddRef & NDRelease. */
    /* This protects any outer object from being prematurely    */
    /* released by an inner object that may have to be created  */
    /* in order to supply the requested interface.              */
    pObj->NonDelegatingAddRef();
    hr = pObj->NonDelegatingQueryInterface(riid, pv);
    pObj->NonDelegatingRelease();
    /* Note that if NonDelegatingQueryInterface fails, it will  */
    /* not increment the ref count, so the NonDelegatingRelease */
    /* will drop the ref back to zero and the object will "self-*/
    /* destruct".  Hence we don't need additional tidy-up code  */
    /* to cope with NonDelegatingQueryInterface failing.        */

    if (SUCCEEDED(hr)) {
        ASSERT(*pv);
    }

    return hr;
}

STDMETHODIMP CClassFactory::LockServer(BOOL fLock)
{
    if (fLock) {
        m_cLocked++;
    } else {
        m_cLocked--;
    }
    return NOERROR;
}


// --- COM entrypoints -----------------------------------------
// DllRegisterServer

//called by COM to get the class factory object for a given class
STDAPI DllGetClassObject(REFCLSID rClsID,REFIID riid,void **pv)
{
	// DebugBreak();

    if (!(riid == IID_IUnknown) && !(riid == IID_IClassFactory)) {
            return E_NOINTERFACE;
    }

    // traverse the array of templates looking for one with this
    // class id
    for (int i = 0; i < g_cTemplates; i++) {
        const CFactoryTemplate * pT = &g_Templates[i];
        if (pT->IsClassID(rClsID)) {

            // found a template - make a class factory based on this
            // template

            *pv = (LPVOID) (LPUNKNOWN) new CClassFactory(pT);
            if (*pv == NULL) {
                return E_OUTOFMEMORY;
            }
            ((LPUNKNOWN)*pv)->AddRef();
            return NOERROR;
        }
    }
    return CLASS_E_CLASSNOTAVAILABLE;
}

//
//  Call any initialization routines
//
void DllInitClasses(BOOL bLoading)
{
   int i;

	// DebugBreak();

	// traverse the array of templates calling the init routine
   // if they have one
   for (i = 0; i < g_cTemplates; i++) {
		const CFactoryTemplate * pT = &g_Templates[i];
      if (pT->m_lpfnInit != NULL) {
			(*pT->m_lpfnInit)(bLoading, pT->m_ClsID);
      }
   }

}

// called by COM to determine if this dll can be unloaded
// return ok unless there are outstanding objects or a lock requested
// by IClassFactory::LockServer
//
// CClassFactory has a static function that can tell us about the locks,
// and CCOMObject has a static function that can tell us about the active
// object count
STDAPI DllCanUnloadNow()
{
	// DebugBreak();

	DbgLog((LOG_MEMORY,2,TEXT("DLLCanUnloadNow called - IsLocked = %d, Active objects = %d"),
        CClassFactory::IsLocked(),
        CBaseObject::ObjectsActive()));

   if (CClassFactory::IsLocked() || CBaseObject::ObjectsActive()) {
	
		return S_FALSE;
   } 
	else {
		return S_OK;
   }
}


// --- standard WIN32 entrypoints --------------------------------------


//extern "C" BOOL WINAPI DllEntryPoint(HINSTANCE, ULONG, LPVOID);
//BOOL WINAPI DllEntryPoint(HINSTANCE hInstance,ULONG ulReason,LPVOID pv)
//BOOL WINAPI DllMain (HINSTANCE hInstance,ULONG ulReason,LPVOID pv)
BOOL WINAPI DllEntryPoint (HINSTANCE hInstance,ULONG ulReason,LPVOID pv)
{

	// DebugBreak();
   
	switch (ulReason) {
	
		case DLL_PROCESS_ATTACH:
			DisableThreadLibraryCalls(hInstance);
			DbgInitialise(hInstance);
			{
				// The platform identifier is used to work out whether
				// full unicode support is available or not.  Hence the
				// default will be the lowest common denominator - i.e. N/A
            g_amPlatform = VER_PLATFORM_WIN32_WINDOWS; // win95 assumed in case GetVersionEx fails

            g_osInfo.dwOSVersionInfoSize = sizeof(g_osInfo);
            if (GetVersionEx(&g_osInfo)) {
        			g_amPlatform = g_osInfo.dwPlatformId;
				} 
				else {
					DbgLog((LOG_ERROR, 1, "Failed to get the OS platform, assuming Win95"));
				}
			}
			hinstance = hInstance;
			DllInitClasses(TRUE);
			
			break;

		case DLL_PROCESS_DETACH:
			DllInitClasses(FALSE);

#ifdef DEBUG
			if (CBaseObject::ObjectsActive()) {
				DbgSetModuleLevel(LOG_MEMORY, 2);
            TCHAR szInfo[512];
            extern TCHAR m_ModuleName[];     // Cut down module name

            TCHAR FullName[_MAX_PATH];      // Load the full path and module name
            TCHAR *pName;                   // Searches from the end for a backslash

            GetModuleFileName(NULL,FullName,_MAX_PATH);
            pName = _tcsrchr(FullName,'\\');
            if (pName == NULL) {
                pName = FullName;
            } 
				else {
					pName++;
            }

				DWORD cch = wsprintf(szInfo, TEXT("Executable: %s  Pid %x  Tid %x. "),
					pName, GetCurrentProcessId(), GetCurrentThreadId());

            wsprintf(szInfo+cch, TEXT("Module %s, %d objects left active!"),
                     m_ModuleName, CBaseObject::ObjectsActive());
            DbgAssert(szInfo, TEXT(__FILE__),__LINE__);

				// If running remotely wait for the Assert to be acknowledged
				// before dumping out the object register
            DbgDumpObjectRegister();
			}
			DbgTerminate();
#endif
			break;
    }
    return TRUE;
}



--- NEW FILE: asiodrvr.h ---
/*
	Steinberg Audio Stream I/O API
	(c) 1996, Steinberg Soft- und Hardware GmbH
	charlie (May 1996)

	asiodrvr.h
	c++ superclass to implement asio functionality. from this,
	you can derive whatever required
*/

#ifndef _asiodrvr_
#define _asiodrvr_

// cpu and os system we are running on
#include "asiosys.h"
// basic "C" interface
#include "asio.h"

class AsioDriver;
extern AsioDriver *getDriver();		// for generic constructor 

#if WINDOWS
#include <windows.h>
#include "combase.h"
#include "iasiodrv.h"
class AsioDriver : public IASIO ,public CUnknown
{
public:
	AsioDriver(LPUNKNOWN pUnk, HRESULT *phr);

	DECLARE_IUNKNOWN
	// Factory method
	static CUnknown *CreateInstance(LPUNKNOWN pUnk, HRESULT *phr);
	// IUnknown
	virtual HRESULT STDMETHODCALLTYPE NonDelegatingQueryInterface(REFIID riid,void **ppvObject);

#else

class AsioDriver
{
public:
	AsioDriver();
#endif
	virtual ~AsioDriver();

	virtual ASIOBool init(void* sysRef);
	virtual void getDriverName(char *name);	// max 32 bytes incl. terminating zero
	virtual long getDriverVersion();
	virtual void getErrorMessage(char *string);	// max 124 bytes incl.

	virtual ASIOError start();
	virtual ASIOError stop();

	virtual ASIOError getChannels(long *numInputChannels, long *numOutputChannels);
	virtual ASIOError getLatencies(long *inputLatency, long *outputLatency);
	virtual ASIOError getBufferSize(long *minSize, long *maxSize,
		long *preferredSize, long *granularity);

	virtual ASIOError canSampleRate(ASIOSampleRate sampleRate);
	virtual ASIOError getSampleRate(ASIOSampleRate *sampleRate);
	virtual ASIOError setSampleRate(ASIOSampleRate sampleRate);
	virtual ASIOError getClockSources(ASIOClockSource *clocks, long *numSources);
	virtual ASIOError setClockSource(long reference);

	virtual ASIOError getSamplePosition(ASIOSamples *sPos, ASIOTimeStamp *tStamp);
	virtual ASIOError getChannelInfo(ASIOChannelInfo *info);

	virtual ASIOError createBuffers(ASIOBufferInfo *bufferInfos, long numChannels,
		long bufferSize, ASIOCallbacks *callbacks);
	virtual ASIOError disposeBuffers();

	virtual ASIOError controlPanel();
	virtual ASIOError future(long selector, void *opt);
	virtual ASIOError outputReady();
};
#endif

--- NEW FILE: ginclude.h ---
#ifndef __gInclude__
#define __gInclude__

#if SGI 
	#undef BEOS 
	#undef MAC 
	#undef WINDOWS
	//
	#define ASIO_BIG_ENDIAN 1
	#define ASIO_CPU_MIPS 1
#elif defined WIN32
	#undef BEOS 
	#undef MAC 
	#undef SGI
	#define WINDOWS 1
	#define ASIO_LITTLE_ENDIAN 1
	#define ASIO_CPU_X86 1
#elif BEOS
	#undef MAC 
	#undef SGI
	#undef WINDOWS
	#define ASIO_LITTLE_ENDIAN 1
	#define ASIO_CPU_X86 1
	//
#else
	#define MAC 1
	#undef BEOS 
	#undef WINDOWS
	#undef SGI
	#define ASIO_BIG_ENDIAN 1
	#define ASIO_CPU_PPC 1
#endif

// always
#define NATIVE_INT64 0
#define IEEE754_64FLOAT 1

#endif	// __gInclude__

--- NEW FILE: asiosys.h ---
#ifndef __asiosys__
	#define __asiosys__

	#ifdef WIN32
		#undef MAC 
		#define PPC 0
		#define WINDOWS 1
		#define SGI 0
		#define SUN 0
		#define LINUX 0
		#define BEOS 0

		#define NATIVE_INT64 0
		#define IEEE754_64FLOAT 1
	
	#elif BEOS
		#define MAC 0
		#define PPC 0
		#define WINDOWS 0
		#define PC 0
		#define SGI 0
		#define SUN 0
		#define LINUX 0
		
		#define NATIVE_INT64 0
		#define IEEE754_64FLOAT 1
		
		#ifndef DEBUG
			#define DEBUG 0
		 	#if DEBUG
		 		void DEBUGGERMESSAGE(char *string);
		 	#else
		  		#define DEBUGGERMESSAGE(a)
			#endif
		#endif

	#elif SGI
		#define MAC 0
		#define PPC 0
		#define WINDOWS 0
		#define PC 0
		#define SUN 0
		#define LINUX 0
		#define BEOS 0
		
		#define NATIVE_INT64 0
		#define IEEE754_64FLOAT 1
		
		#ifndef DEBUG
			#define DEBUG 0
		 	#if DEBUG
		 		void DEBUGGERMESSAGE(char *string);
		 	#else
		  		#define DEBUGGERMESSAGE(a)
			#endif
		#endif

	#else	// MAC

		#define MAC 1
		#define PPC 1
		#define WINDOWS 0
		#define PC 0
		#define SGI 0
		#define SUN 0
		#define LINUX 0
		#define BEOS 0

		#define NATIVE_INT64 0
		#define IEEE754_64FLOAT 1

		#ifndef DEBUG
			#define DEBUG 0
			#if DEBUG
				void DEBUGGERMESSAGE(char *string);
			#else
				#define DEBUGGERMESSAGE(a)
			#endif
		#endif
	#endif

#endif

--- NEW FILE: wxdebug.h ---
//==========================================================================;
//
//  THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
//  KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
//  IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
//  PURPOSE.
//
//  Copyright (c) 1992 - 1996  Microsoft Corporation.  All Rights Reserved.
//
//--------------------------------------------------------------------------;

// Debugging facilities, January 1995

#ifndef __WXDEBUG__
#define __WXDEBUG__

// Avoid conflict with MFC
#undef ASSERT

// This library provides fairly straight forward debugging functionality, this
// is split into two main sections. The first is assertion handling, there are
// three types of assertions provided here. The most commonly used one is the
// ASSERT(condition) macro which will pop up a message box including the file
// and line number if the condition evaluates to FALSE. Then there is the
// EXECUTE_ASSERT macro which is the same as ASSERT except the condition will
// still be executed in NON debug builds. The final type of assertion is the
// KASSERT macro which is more suitable for pure (perhaps kernel) filters as
// the condition is printed onto the debugger rather than in a message box.
//
// The other part of the debug module facilties is general purpose logging.
// This is accessed by calling DbgLog(). The function takes a type and level
// field which define the type of informational string you are presenting and
// it's relative importance. The type field can be a combination (one or more)
// of LOG_TIMING, LOG_TRACE, LOG_MEMORY, LOG_LOCKING and LOG_ERROR. The level
// is a DWORD value where zero defines highest important. Use of zero as the
// debug logging level is to be encouraged ONLY for major errors or events as
// they will ALWAYS be displayed on the debugger. Other debug output has it's
// level matched against the current debug output level stored in the registry
// for this module and if less than the current setting it will be displayed.
//
// Each module or executable has it's own debug output level for each of the
// five types. These are read in when the DbgInitialise function is called
// for DLLs linking to STRMBASE.LIB this is done automatically when the DLL
// is loaded, executables must call it explicitely with the module instance
// handle given to them through the WINMAIN entry point. An executable must
// also call DbgTerminate when they have finished to clean up the resources
// the debug library uses, once again this is done automatically for DLLs

// These are the five different categories of logging information

enum {  LOG_TIMING = 0x01,    // Timing and performance measurements
        LOG_TRACE = 0x02,     // General step point call tracing
        LOG_MEMORY =  0x04,   // Memory and object allocation/destruction
        LOG_LOCKING = 0x08,   // Locking/unlocking of critical sections
        LOG_ERROR = 0x10 };   // Debug error notification

enum {  CDISP_HEX = 0x01,
        CDISP_DEC = 0x02};

// For each object created derived from CBaseObject (in debug builds) we
// create a descriptor that holds it's name (statically allocated memory)
// and a cookie we assign it. We keep a list of all the active objects
// we have registered so that we can dump a list of remaining objects

typedef struct tag_ObjectDesc {
    TCHAR *m_pName;
    DWORD m_dwCookie;
    tag_ObjectDesc *m_pNext;
} ObjectDesc;

#define DLLIMPORT __declspec(dllimport)
#define DLLEXPORT __declspec(dllexport)

#ifdef DEBUG

    #define NAME(x) TEXT(x)

    // These are used internally by the debug library (PRIVATE)

    void DbgInitKeyLevels(HKEY hKey);
    void DbgInitGlobalSettings();
    void DbgInitModuleSettings();
    void DbgInitModuleName();
    DWORD DbgRegisterObjectCreation(TCHAR *pObjectName);
    BOOL DbgRegisterObjectDestruction(DWORD dwCookie);

    // These are the PUBLIC entry points

    BOOL DbgCheckModuleLevel(DWORD Type,DWORD Level);
    void DbgSetModuleLevel(DWORD Type,DWORD Level);

    // Initialise the library with the module handle

    void DbgInitialise(HINSTANCE hInst);
    void DbgTerminate();

    void DbgDumpObjectRegister();

    // Display error and logging to the user

    void DbgAssert(const TCHAR *pCondition,const TCHAR *pFileName,INT iLine);
    void DbgBreakPoint(const TCHAR *pCondition,const TCHAR *pFileName,INT iLine);
    void DbgKernelAssert(const TCHAR *pCondition,const TCHAR *pFileName,INT iLine);
    void DbgLogInfo(DWORD Type,DWORD Level,const TCHAR *pFormat,...);
    void DbgOutString(LPCTSTR psz);

    //  Debug infinite wait stuff
    DWORD DbgWaitForSingleObject(HANDLE h);
    DWORD DbgWaitForMultipleObjects(DWORD nCount,
                                    CONST HANDLE *lpHandles,
                                    BOOL bWaitAll);
    void DbgSetWaitTimeout(DWORD dwTimeout);

#ifdef __strmif_h__
    void DisplayType(LPSTR label, const AM_MEDIA_TYPE *pmtIn);
#endif

    #define KASSERT(_x_) if (!(_x_))         \
        DbgKernelAssert(TEXT(#_x_),TEXT(__FILE__),__LINE__)

    //  Break on the debugger without putting up a message box
    //  message goes to debugger instead

    #define KDbgBreak(_x_)                   \
        DbgKernelAssert(TEXT(#_x_),TEXT(__FILE__),__LINE__)

    #define ASSERT(_x_) if (!(_x_))         \
        DbgAssert(TEXT(#_x_),TEXT(__FILE__),__LINE__)

    //  Put up a message box informing the user of a halt
    //  condition in the program

    #define DbgBreak(_x_)                   \
        DbgBreakPoint(TEXT(#_x_),TEXT(__FILE__),__LINE__)

    #define EXECUTE_ASSERT(_x_) ASSERT(_x_)
    #define DbgLog(_x_) DbgLogInfo _x_

    // MFC style trace macros

    #define NOTE(_x_)             DbgLog((LOG_TRACE,5,TEXT(_x_)));
    #define NOTE1(_x_,a)          DbgLog((LOG_TRACE,5,TEXT(_x_),a));
    #define NOTE2(_x_,a,b)        DbgLog((LOG_TRACE,5,TEXT(_x_),a,b));
    #define NOTE3(_x_,a,b,c)      DbgLog((LOG_TRACE,5,TEXT(_x_),a,b,c));
    #define NOTE4(_x_,a,b,c,d)    DbgLog((LOG_TRACE,5,TEXT(_x_),a,b,c,d));
    #define NOTE5(_x_,a,b,c,d,e)  DbgLog((LOG_TRACE,5,TEXT(_x_),a,b,c,d,e));

#else

    // Retail builds make public debug functions inert  - WARNING the source
    // files do not define or build any of the entry points in debug builds
    // (public entry points compile to nothing) so if you go trying to call
    // any of the private entry points in your source they won't compile

    #define NAME(_x_) NULL

    #define DbgInitialise(hInst)
    #define DbgTerminate()
    #define DbgLog(_x_)
    #define DbgOutString(psz)

    #define DbgRegisterObjectCreation(pObjectName)
    #define DbgRegisterObjectDestruction(dwCookie)
    #define DbgDumpObjectRegister()

    #define DbgCheckModuleLevel(Type,Level)
    #define DbgSetModuleLevel(Type,Level)

    #define DbgWaitForSingleObject(h)  WaitForSingleObject(h, INFINITE)
    #define DbgWaitForMultipleObjects(nCount, lpHandles, bWaitAll)     \
               WaitForMultipleObjects(nCount, lpHandles, bWaitAll, INFINITE)
    #define DbgSetWaitTimeout(dwTimeout)

    #define KDbgBreak(_x_)
    #define DbgBreak(_x_)

    #define KASSERT(_x_)
    #define ASSERT(_x_)
    #define EXECUTE_ASSERT(_x_) _x_

    // MFC style trace macros

    #define NOTE(_x_)
    #define NOTE1(_x_,a)
    #define NOTE2(_x_,a,b)
    #define NOTE3(_x_,a,b,c)
    #define NOTE4(_x_,a,b,c,d)
    #define NOTE5(_x_,a,b,c,d,e)

    #define DisplayType(label, pmtIn)

#endif


// Checks a pointer which should be non NULL - can be used as follows.

#define CheckPointer(p,ret) {if((p)==NULL) return (ret);}

//   HRESULT Foo(VOID *pBar)
//   {
//       CheckPointer(pBar,E_INVALIDARG)
//   }
//
//   Or if the function returns a boolean
//
//   BOOL Foo(VOID *pBar)
//   {
//       CheckPointer(pBar,FALSE)
//   }

// These validate pointers when symbol VFWROBUST is defined
// This will normally be defined in debug not retail builds

#ifdef DEBUG
    #define VFWROBUST
#endif

#ifdef VFWROBUST

    #define ValidateReadPtr(p,cb) \
        {if(IsBadReadPtr((PVOID)p,cb) == TRUE) \
            DbgBreak("Invalid read pointer");}

    #define ValidateWritePtr(p,cb) \
        {if(IsBadWritePtr((PVOID)p,cb) == TRUE) \
            DbgBreak("Invalid write pointer");}

    #define ValidateReadWritePtr(p,cb) \
        {ValidateReadPtr(p,cb) ValidateWritePtr(p,cb)}

    #define ValidateStringPtr(p) \
        {if(IsBadStringPtr((LPCTSTR)p,INFINITE) == TRUE) \
            DbgBreak("Invalid string pointer");}

    #define ValidateStringPtrA(p) \
        {if(IsBadStringPtrA((LPCSTR)p,INFINITE) == TRUE) \
            DbgBreak("Invalid ANSII string pointer");}

    #define ValidateStringPtrW(p) \
        {if(IsBadStringPtrW((LPCWSTR)p,INFINITE) == TRUE) \
            DbgBreak("Invalid UNICODE string pointer");}

#else
    #define ValidateReadPtr(p,cb)
    #define ValidateWritePtr(p,cb)
    #define ValidateReadWritePtr(p,cb)
    #define ValidateStringPtr(p)
    #define ValidateStringPtrA(p)
    #define ValidateStringPtrW(p)
#endif


#ifdef _OBJBASE_H_

    //  Outputting GUID names.  If you want to include the name
    //  associated with a GUID (eg CLSID_...) then
    //
    //      GuidNames[yourGUID]
    //
    //  Returns the name defined in uuids.h as a string

    typedef struct {
        TCHAR   *szName;
        GUID    guid;
    } GUID_STRING_ENTRY;

    class CGuidNameList {
    public:
        TCHAR *operator [] (const GUID& guid);
    };

    extern CGuidNameList GuidNames;

#endif


//  REMIND macro - generates warning as reminder to complete coding
//  (eg) usage:
//
//  #pragma message (REMIND("Add automation support"))


#define QUOTE(x) #x
#define QQUOTE(y) QUOTE(y)
#define REMIND(str) __FILE__ "(" QQUOTE(__LINE__) ") :  " str


//  Hack to display objects in a useful format
//
//  eg If you want to display a LONGLONG ll in a debug string do (eg)
//
//  DbgLog((LOG_TRACE, n, TEXT("Value is %s"), (LPCTSTR)CDisp(ll, CDISP_HEX)));


class CDispBasic
{
public:
    CDispBasic() { m_pString = m_String; };
    ~CDispBasic();
protected:
    PTCHAR m_pString;  // normally points to m_String... unless too much data
    TCHAR m_String[50];
};
class CDisp : public CDispBasic
{
public:
    CDisp(LONGLONG ll, int Format = CDISP_HEX); // Display a LONGLONG in CDISP_HEX or CDISP_DEC form
    CDisp(REFCLSID clsid);      // Display a GUID
    CDisp(double d);            // Display a floating point number
#ifdef __strmif_h__
#ifdef __STREAMS__
    CDisp(CRefTime t);          // Display a Reference Time
#endif
    CDisp(IPin *pPin);          // Display a pin as {filter clsid}(pin name)
#endif // __strmif_h__
    ~CDisp();

    //  Implement cast to (LPCTSTR) as parameter to logger
    operator LPCTSTR()
    {
        return (LPCTSTR)m_pString;
    };
};

#endif // __WXDEBUG__


--- NEW FILE: combase.h ---
//==========================================================================;
//
//  THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
//  KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
//  IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
//  PURPOSE.
//
//  Copyright (c) 1992 - 1996  Microsoft Corporation.  All Rights Reserved.
//
//--------------------------------------------------------------------------;

// Base class hierachy for creating COM objects, December 1994

/*

a. Derive your COM object from CUnknown

b. Make a static CreateInstance function that takes an LPUNKNOWN, an HRESULT *
   and a TCHAR *. The LPUNKNOWN defines the object to delegate IUnknown calls
   to. The HRESULT * allows error codes to be passed around constructors and
   the TCHAR * is a descriptive name that can be printed on the debugger.

   It is important that constructors only change the HRESULT * if they have
   to set an ERROR code, if it was successful then leave it alone or you may
   overwrite an error code from an object previously created.

   When you call a constructor the descriptive name should be in static store
   as we do not copy the string. To stop large amounts of memory being used
   in retail builds by all these static strings use the NAME macro,

   CMyFilter = new CImplFilter(NAME("My filter"),pUnknown,phr);
   if (FAILED(hr)) {
       return hr;
   }

   In retail builds NAME(_x_) compiles to NULL, the base CBaseObject class
   knows not to do anything with objects that don't have a name.

c. Have a constructor for your object that passes the LPUNKNOWN, HRESULT * and
   TCHAR * to the CUnknown constructor. You can set the HRESULT if you have an
   error, or just simply pass it through to the constructor.

   The object creation will fail in the class factory if the HRESULT indicates
   an error (ie FAILED(HRESULT) == TRUE)

d. Create a FactoryTemplate with your object's class id and CreateInstance
   function.

Then (for each interface) either

Multiple inheritance

1. Also derive it from ISomeInterface
2. Include DECLARE_IUNKNOWN in your class definition to declare
   implementations of QueryInterface, AddRef and Release that
   call the outer unknown
3. Override NonDelegatingQueryInterface to expose ISomeInterface by
   code something like

     if (riid == IID_ISomeInterface) {
         return GetInterface((ISomeInterface *) this, ppv);
     } else {
         return CUnknown::NonDelegatingQueryInterface(riid, ppv);
     }

4. Declare and implement the member functions of ISomeInterface.

or: Nested interfaces

1. Declare a class derived from CUnknown
2. Include DECLARE_IUNKNOWN in your class definition
3. Override NonDelegatingQueryInterface to expose ISomeInterface by
   code something like

     if (riid == IID_ISomeInterface) {
         return GetInterface((ISomeInterface *) this, ppv);
     } else {
         return CUnknown::NonDelegatingQueryInterface(riid, ppv);
     }

4. Implement the member functions of ISomeInterface. Use GetOwner() to
   access the COM object class.

And in your COM object class:

5. Make the nested class a friend of the COM object class, and declare
   an instance of the nested class as a member of the COM object class.

   NOTE that because you must always pass the outer unknown and an hResult
   to the CUnknown constructor you cannot use a default constructor, in
   other words you will have to make the member variable a pointer to the
   class and make a NEW call in your constructor to actually create it.

6. override the NonDelegatingQueryInterface with code like this:

     if (riid == IID_ISomeInterface) {
         return m_pImplFilter->
            NonDelegatingQueryInterface(IID_ISomeInterface, ppv);
     } else {
         return CUnknown::NonDelegatingQueryInterface(riid, ppv);
     }

You can have mixed classes which support some interfaces via multiple
inheritance and some via nested classes

*/

#ifndef __COMBASE__
#define __COMBASE__

/* The DLLENTRY module initialises the module handle on loading */

extern HINSTANCE g_hInst;

/* On DLL load remember which platform we are running on */

extern DWORD g_amPlatform;
extern OSVERSIONINFO g_osInfo;     // Filled in by GetVersionEx

/* Version of IUnknown that is renamed to allow a class to support both
   non delegating and delegating IUnknowns in the same COM object */

DECLARE_INTERFACE(INonDelegatingUnknown)
{
    STDMETHOD(NonDelegatingQueryInterface) (THIS_ REFIID, LPVOID *) PURE;
    STDMETHOD_(ULONG, NonDelegatingAddRef)(THIS) PURE;
    STDMETHOD_(ULONG, NonDelegatingRelease)(THIS) PURE;
};

typedef INonDelegatingUnknown *PNDUNKNOWN;


/* This is the base object class that supports active object counting. As
   part of the debug facilities we trace every time a C++ object is created
   or destroyed. The name of the object has to be passed up through the class
   derivation list during construction as you cannot call virtual functions
   in the constructor. The downside of all this is that every single object
   constructor has to take an object name parameter that describes it */

class CBaseObject
{

private:

    // Disable the copy constructor and assignment by default so you will get
    //   compiler errors instead of unexpected behaviour if you pass objects
    //   by value or assign objects.
    CBaseObject(const CBaseObject& objectSrc);          // no implementation
    void operator=(const CBaseObject& objectSrc);       // no implementation

private:
    static LONG m_cObjects;     /* Total number of objects active */

protected:
#ifdef DEBUG
    DWORD m_dwCookie;           /* Cookie identifying this object */
#endif


public:

    /* These increment and decrement the number of active objects */

    CBaseObject(TCHAR *pName);
    ~CBaseObject();

    /* Call this to find if there are any CUnknown derived objects active */

    static LONG ObjectsActive() {
        return m_cObjects;
    };
};


/* An object that supports one or more COM interfaces will be based on
   this class. It supports counting of total objects for DLLCanUnloadNow
   support, and an implementation of the core non delegating IUnknown */

class CUnknown : public INonDelegatingUnknown,
                 public CBaseObject
{

private:
    const LPUNKNOWN m_pUnknown; /* Owner of this object */

protected:                      /* So we can override NonDelegatingRelease() */
    volatile LONG m_cRef;       /* Number of reference counts */

public:

    CUnknown(TCHAR *pName, LPUNKNOWN pUnk, HRESULT *phr);
    virtual ~CUnknown() {};

    /* Return the owner of this object */

    LPUNKNOWN GetOwner() const {
        return m_pUnknown;
    };

    /* Called from the class factory to create a new instance, it is
       pure virtual so it must be overriden in your derived class */

    /* static CUnknown *CreateInstance(LPUNKNOWN, HRESULT *) */

    /* Non delegating unknown implementation */

    STDMETHODIMP NonDelegatingQueryInterface(REFIID, void **);
    STDMETHODIMP_(ULONG) NonDelegatingAddRef();
    STDMETHODIMP_(ULONG) NonDelegatingRelease();

    /* Return an interface pointer to a requesting client
       performing a thread safe AddRef as necessary */

    HRESULT GetInterface(LPUNKNOWN pUnk, void **ppv);


};

/* The standard InterlockedXXX functions won't take volatiles */
static inline LONG InterlockedIncrement( volatile LONG * plong )
{ return InterlockedIncrement( const_cast<LONG*>( plong ) ); }

static inline LONG InterlockedDecrement( volatile LONG * plong )
{ return InterlockedDecrement( const_cast<LONG*>( plong ) ); }

static inline LONG InterlockedExchange( volatile LONG * plong, LONG new_value )
{ return InterlockedExchange( const_cast<LONG*>( plong ), new_value ); }


/* A function that can create a new COM object */

typedef CUnknown *(*LPFNNewCOMObject)(LPUNKNOWN pUnkOuter, HRESULT *phr);

/*  A function (can be NULL) which is called from the DLL entrypoint
    routine for each factory template:

    bLoading - TRUE on DLL load, FALSE on DLL unload
    rclsid   - the m_ClsID of the entry
*/
typedef void (*LPFNInitRoutine)(BOOL bLoading, const CLSID *rclsid);

/* Create one of these per object class in an array so that
   the default class factory code can create new instances */

class CFactoryTemplate {

public:

    const WCHAR *m_Name;
    const CLSID *m_ClsID;
    LPFNNewCOMObject m_lpfnNew;
    LPFNInitRoutine  m_lpfnInit;

    BOOL IsClassID(REFCLSID rclsid) const {
        return (IsEqualCLSID(*m_ClsID,rclsid));
    };

    CUnknown *CreateInstance(LPUNKNOWN pUnk, HRESULT *phr) const {
        return m_lpfnNew(pUnk, phr);
    };
};


/* You must override the (pure virtual) NonDelegatingQueryInterface to return
   interface pointers (using GetInterface) to the interfaces your derived
   class supports (the default implementation only supports IUnknown) */

#define DECLARE_IUNKNOWN                                        \
    STDMETHODIMP QueryInterface(REFIID riid, void **ppv) {      \
        return GetOwner()->QueryInterface(riid,ppv);            \
    };                                                          \
    STDMETHODIMP_(ULONG) AddRef() {                             \
        return GetOwner()->AddRef();                            \
    };                                                          \
    STDMETHODIMP_(ULONG) Release() {                            \
        return GetOwner()->Release();                           \
    };

#endif /* __COMBASE__ */



--- NEW FILE: asiosmpl.h ---
/*
	Steinberg Audio Stream I/O API
	(c) 1999, Steinberg Soft- und Hardware GmbH

	asiosmpl.h
	
	test implementation of asio
*/

#ifndef _asiosmpl_
#define _asiosmpl_

#include "asiosys.h"

#define TESTWAVES 1
// when true, will feed the left input (to host) with
// a sine wave, and the right one with a sawtooth

enum
{
	kBlockFrames = 256,
	kNumInputs = 16,
	kNumOutputs = 16
};

#if WINDOWS

#include "rpc.h"
#include "rpcndr.h"
#ifndef COM_NO_WINDOWS_H
#include <windows.h>
#include "ole2.h"
#endif

#include "combase.h"
#include "iasiodrv.h"

class AsioSample : public IASIO, public CUnknown
{
public:
	AsioSample(LPUNKNOWN pUnk, HRESULT *phr);
	~AsioSample();

	DECLARE_IUNKNOWN
    //STDMETHODIMP QueryInterface(REFIID riid, void **ppv) {      \
    //    return GetOwner()->QueryInterface(riid,ppv);            \
    //};                                                          \
    //STDMETHODIMP_(ULONG) AddRef() {                             \
    //    return GetOwner()->AddRef();                            \
    //};                                                          \
    //STDMETHODIMP_(ULONG) Release() {                            \
    //    return GetOwner()->Release();                           \
    //};

	// Factory method
	static CUnknown *CreateInstance(LPUNKNOWN pUnk, HRESULT *phr);
	// IUnknown
	virtual HRESULT STDMETHODCALLTYPE NonDelegatingQueryInterface(REFIID riid,void **ppvObject);
#else

#include "asiodrvr.h"

//---------------------------------------------------------------------------------------------
class AsioSample : public AsioDriver
{
public:
	AsioSample ();
	~AsioSample ();
#endif

	ASIOBool init (void* sysRef);
	void getDriverName (char *name);	// max 32 bytes incl. terminating zero
	long getDriverVersion ();
	void getErrorMessage (char *string);	// max 128 bytes incl.

	ASIOError start ();
	ASIOError stop ();

	ASIOError getChannels (long *numInputChannels, long *numOutputChannels);
	ASIOError getLatencies (long *inputLatency, long *outputLatency);
	ASIOError getBufferSize (long *minSize, long *maxSize,
		long *preferredSize, long *granularity);

	ASIOError canSampleRate (ASIOSampleRate sampleRate);
	ASIOError getSampleRate (ASIOSampleRate *sampleRate);
	ASIOError setSampleRate (ASIOSampleRate sampleRate);
	ASIOError getClockSources (ASIOClockSource *clocks, long *numSources);
	ASIOError setClockSource (long index);

	ASIOError getSamplePosition (ASIOSamples *sPos, ASIOTimeStamp *tStamp);
	ASIOError getChannelInfo (ASIOChannelInfo *info);

	ASIOError createBuffers (ASIOBufferInfo *bufferInfos, long numChannels,
		long bufferSize, ASIOCallbacks *callbacks);
	ASIOError disposeBuffers ();

	ASIOError controlPanel ();
	ASIOError future (long selector, void *opt);
	ASIOError outputReady ();

	void bufferSwitch ();
	long getMilliSeconds () {return milliSeconds;}

private:
friend void myTimer();

	bool inputOpen ();
#if TESTWAVES
	void makeSine (short *wave);
	void makeSaw (short *wave);
#endif
	void inputClose ();
	void input ();

	bool outputOpen ();
	void outputClose ();
	void output ();

	void timerOn ();
	void timerOff ();
	void bufferSwitchX ();

	double samplePosition;
	double sampleRate;
	ASIOCallbacks *callbacks;
	ASIOTime asioTime;
	ASIOTimeStamp theSystemTime;
	short *inputBuffers[kNumInputs * 2];
	short *outputBuffers[kNumOutputs * 2];
#if TESTWAVES
	short *sineWave, *sawTooth;
#endif
	long inMap[kNumInputs];
	long outMap[kNumOutputs];
	long blockFrames;
	long inputLatency;
	long outputLatency;
	long activeInputs;
	long activeOutputs;
	long toggle;
	long milliSeconds;
	bool active, started;
	bool timeInfoMode, tcRead;
	char errorMessage[128];
};

#endif






More information about the Pd-cvs mailing list