添加一些关于midi播放的项目
This commit is contained in:
@@ -0,0 +1,572 @@
|
||||
/*
|
||||
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 <[email protected]>
|
||||
|
||||
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__)) && !defined(_WIN64)
|
||||
|
||||
|
||||
// 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 */ \
|
||||
: "%edx" /* Clobbered Registers */ \
|
||||
); \
|
||||
|
||||
|
||||
#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) \
|
||||
: "%edx" /* Clobbered Registers */ \
|
||||
); \
|
||||
|
||||
|
||||
#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) \
|
||||
: "%edx" /* Clobbered Registers */ \
|
||||
); \
|
||||
|
||||
|
||||
#define CALL_THISCALL_1_DOUBLE( resultName, thisPtr, funcOffset, param1 ) \
|
||||
do { \
|
||||
double param1f64 = param1; /* Cast explicitly to double */ \
|
||||
double *param1f64Ptr = ¶m1f64; /* Make pointer to address */ \
|
||||
__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 */ \
|
||||
: "r"(param1f64Ptr), /* Input Operands */ \
|
||||
"c"(thisPtr), \
|
||||
"m"(*param1f64Ptr) /* Using address */ \
|
||||
: "%edx" /* Clobbered Registers */ \
|
||||
); \
|
||||
} while (0); \
|
||||
|
||||
|
||||
#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) \
|
||||
: "%edx" /* Clobbered Registers */ \
|
||||
); \
|
||||
|
||||
|
||||
#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) \
|
||||
: "%edx" /* Clobbered Registers */ \
|
||||
); \
|
||||
|
||||
#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_ in case 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 */
|
||||
|
||||
Reference in New Issue
Block a user