diff options
| author | Gary Scavone <gary@music.mcgill.ca> | 2013-10-09 23:50:06 +0200 |
|---|---|---|
| committer | Stephen Sinclair <sinclair@music.mcgill.ca> | 2013-10-10 01:16:30 +0200 |
| commit | 0fbcd74a04713a4725d2f346a493121b623d60ab (patch) | |
| tree | 9a65cf44aa5b6cf2d38352fe8336044f5452670d /asio | |
| parent | fdc3f15bec57b30fae67f65270392ba7a86680b8 (diff) | |
Version 3.0.3
Diffstat (limited to 'asio')
| -rw-r--r-- | asio/asio.cpp | 257 | ||||
| -rw-r--r-- | asio/asio.h | 115 | ||||
| -rw-r--r-- | asio/asiodrivers.cpp | 186 | ||||
| -rw-r--r-- | asio/asiodrvr.h | 76 | ||||
| -rw-r--r-- | asio/asiolist.cpp | 268 | ||||
| -rw-r--r-- | asio/iasiodrv.h | 37 | ||||
| -rw-r--r-- | asio/iasiothiscallresolver.cpp | 563 | ||||
| -rw-r--r-- | asio/iasiothiscallresolver.h | 201 |
8 files changed, 1695 insertions, 8 deletions
diff --git a/asio/asio.cpp b/asio/asio.cpp new file mode 100644 index 0000000..b241663 --- /dev/null +++ b/asio/asio.cpp @@ -0,0 +1,257 @@ +/*
+ 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
+
+
diff --git a/asio/asio.h b/asio/asio.h index 3003130..8ec811f 100644 --- a/asio/asio.h +++ b/asio/asio.h @@ -3,9 +3,12 @@ /*
Steinberg Audio Stream I/O API
- (c) 1997 - 1999, Steinberg Soft- und Hardware GmbH
+ (c) 1997 - 2005, Steinberg Media Technologies GmbH
+
+ ASIO Interface Specification v 2.1
+
+ 2005 - Added support for DSD sample data (in cooperation with Sony)
- ASIO Interface Specification v 2.0
basic concept is an i/o synchronous double-buffer scheme:
@@ -131,7 +134,7 @@ enum { // 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
+ ASIOSTInt32MSB16 = 8, // 32 bit data with 16 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
@@ -147,9 +150,56 @@ enum { 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
+ ASIOSTInt32LSB24 = 27, // 32 bit data with 24 bit alignment
+
+ // ASIO DSD format.
+ ASIOSTDSDInt8LSB1 = 32, // DSD 1 bit data, 8 samples per byte. First sample in Least significant bit.
+ ASIOSTDSDInt8MSB1 = 33, // DSD 1 bit data, 8 samples per byte. First sample in Most significant bit.
+ ASIOSTDSDInt8NER8 = 40, // DSD 8 bit data, 1 sample per byte. No Endianness required.
+
+ ASIOSTLastEntry
};
+/*-----------------------------------------------------------------------------
+// DSD operation and buffer layout
+// Definition by Steinberg/Sony Oxford.
+//
+// We have tried to treat DSD as PCM and so keep a consistant structure across
+// the ASIO interface.
+//
+// DSD's sample rate is normally referenced as a multiple of 44.1Khz, so
+// the standard sample rate is refered to as 64Fs (or 2.8224Mhz). We looked
+// at making a special case for DSD and adding a field to the ASIOFuture that
+// would allow the user to select the Over Sampleing Rate (OSR) as a seperate
+// entity but decided in the end just to treat it as a simple value of
+// 2.8224Mhz and use the standard interface to set it.
+//
+// The second problem was the "word" size, in PCM the word size is always a
+// greater than or equal to 8 bits (a byte). This makes life easy as we can
+// then pack the samples into the "natural" size for the machine.
+// In DSD the "word" size is 1 bit. This is not a major problem and can easily
+// be dealt with if we ensure that we always deal with a multiple of 8 samples.
+//
+// DSD brings with it another twist to the Endianness religion. How are the
+// samples packed into the byte. It would be nice to just say the most significant
+// bit is always the first sample, however there would then be a performance hit
+// on little endian machines. Looking at how some of the processing goes...
+// Little endian machines like the first sample to be in the Least Significant Bit,
+// this is because when you write it to memory the data is in the correct format
+// to be shifted in and out of the words.
+// Big endian machine prefer the first sample to be in the Most Significant Bit,
+// again for the same reasion.
+//
+// And just when things were looking really muddy there is a proposed extension to
+// DSD that uses 8 bit word sizes. It does not care what endianness you use.
+//
+// Switching the driver between DSD and PCM mode
+// ASIOFuture allows for extending the ASIO API quite transparently.
+// See kAsioSetIoFormat, kAsioGetIoFormat, kAsioCanDoIoFormat
+//
+//-----------------------------------------------------------------------------*/
+
+
//- - - - - - - - - - - - - - - - - - - - - - - - -
// Error codes
//- - - - - - - - - - - - - - - - - - - - - - - - -
@@ -403,9 +453,14 @@ enum 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
+ kAsioSupportsTimeCode, //
+ kAsioMMCCommand, // unused - value: number of commands, message points to mmc commands
+ kAsioSupportsInputMonitor, // kAsioSupportsXXX return 1 if host supports this
+ kAsioSupportsInputGain, // unused and undefined
+ kAsioSupportsInputMeter, // unused and undefined
+ kAsioSupportsOutputGain, // unused and undefined
+ kAsioSupportsOutputMeter, // unused and undefined
+ kAsioOverload, // driver detected an overload
kAsioNumMessageSelectors
};
@@ -860,7 +915,14 @@ enum kAsioCanInputGain,
kAsioCanInputMeter,
kAsioCanOutputGain,
- kAsioCanOutputMeter
+ kAsioCanOutputMeter,
+
+ // DSD support
+ // The following extensions are required to allow switching
+ // and control of the DSD subsystem.
+ kAsioSetIoFormat = 0x23111961, /* ASIOIoFormat * in params. */
+ kAsioGetIoFormat = 0x23111983, /* ASIOIoFormat * in params. */
+ kAsioCanDoIoFormat = 0x23112004, /* ASIOIoFormat * in params. */
};
typedef struct ASIOInputMonitor
@@ -905,6 +967,43 @@ enum kTransMonitor // trackSwitches
};
+/*
+// DSD support
+// Some notes on how to use ASIOIoFormatType.
+//
+// The caller will fill the format with the request types.
+// If the board can do the request then it will leave the
+// values unchanged. If the board does not support the
+// request then it will change that entry to Invalid (-1)
+//
+// So to request DSD then
+//
+// ASIOIoFormat NeedThis={kASIODSDFormat};
+//
+// if(ASE_SUCCESS != ASIOFuture(kAsioSetIoFormat,&NeedThis) ){
+// // If the board did not accept one of the parameters then the
+// // whole call will fail and the failing parameter will
+// // have had its value changes to -1.
+// }
+//
+// Note: Switching between the formats need to be done before the "prepared"
+// state (see ASIO 2 documentation) is entered.
+*/
+typedef long int ASIOIoFormatType;
+enum ASIOIoFormatType_e
+{
+ kASIOFormatInvalid = -1,
+ kASIOPCMFormat = 0,
+ kASIODSDFormat = 1,
+};
+
+typedef struct ASIOIoFormat_s
+{
+ ASIOIoFormatType FormatType;
+ char future[512-sizeof(ASIOIoFormatType)];
+} ASIOIoFormat;
+
+
ASIOError ASIOOutputReady(void);
/* Purpose:
this tells the driver that the host has completed processing
diff --git a/asio/asiodrivers.cpp b/asio/asiodrivers.cpp new file mode 100644 index 0000000..5f56454 --- /dev/null +++ b/asio/asiodrivers.cpp @@ -0,0 +1,186 @@ +#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
diff --git a/asio/asiodrvr.h b/asio/asiodrvr.h new file mode 100644 index 0000000..663f75a --- /dev/null +++ b/asio/asiodrvr.h @@ -0,0 +1,76 @@ +/*
+ 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
diff --git a/asio/asiolist.cpp b/asio/asiolist.cpp new file mode 100644 index 0000000..5a62f5b --- /dev/null +++ b/asio/asiolist.cpp @@ -0,0 +1,268 @@ +#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;
+}
+
+
diff --git a/asio/iasiodrv.h b/asio/iasiodrv.h new file mode 100644 index 0000000..64d2dbb --- /dev/null +++ b/asio/iasiodrv.h @@ -0,0 +1,37 @@ +#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;
+};
diff --git a/asio/iasiothiscallresolver.cpp b/asio/iasiothiscallresolver.cpp new file mode 100644 index 0000000..38c39d2 --- /dev/null +++ b/asio/iasiothiscallresolver.cpp @@ -0,0 +1,563 @@ +/*
+ 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@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_ (¶m1); \
+ __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"(¶m1), /* 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 */
+
diff --git a/asio/iasiothiscallresolver.h b/asio/iasiothiscallresolver.h new file mode 100644 index 0000000..b59d910 --- /dev/null +++ b/asio/iasiothiscallresolver.h @@ -0,0 +1,201 @@ +// ****************************************************************************
+//
+// Changed: I have modified this file slightly (includes) to work with
+// RtAudio. RtAudio.cpp must include this file after asio.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 "iasiodrv.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 */
+
+
|
