moved trunk into place
This commit is contained in:
commit
c1e1d067d2
260 changed files with 96276 additions and 0 deletions
140
src/hostapi/asio/ASIO-README.txt
Normal file
140
src/hostapi/asio/ASIO-README.txt
Normal file
|
|
@ -0,0 +1,140 @@
|
|||
ASIO-README.txt
|
||||
|
||||
This document contains information to help you compile PortAudio with
|
||||
ASIO support. If you find any omissions or errors in this document
|
||||
please notify us on the PortAudio mailing list.
|
||||
|
||||
|
||||
Building PortAudio with ASIO support
|
||||
------------------------------------
|
||||
|
||||
To build PortAudio with ASIO support you need to compile and link with
|
||||
pa_asio.c, and files from the ASIO SDK (see below), along with the common
|
||||
files from src/common/ and platform specific files from src/os/win/ (for Win32)
|
||||
or src/os/mac/ (for Macintosh).
|
||||
|
||||
If you are compiling with a non-Microsoft compiler on Windows, also
|
||||
compile and link with iasiothiscallresolver.cpp (see below for
|
||||
an explanation).
|
||||
|
||||
For some platforms (MingW, possibly Mac), you may simply
|
||||
be able to type:
|
||||
|
||||
./configure --with-host_os=mingw --with-winapi=asio [--with-asiodir=/usr/local/asiosdk2]
|
||||
make
|
||||
|
||||
./configure --with-host_os=darwin --with-winapi=asio [--with-asiodir=/usr/local/asiosdk2]
|
||||
make
|
||||
|
||||
and life will be good.
|
||||
|
||||
|
||||
Obtaining the ASIO SDK
|
||||
----------------------
|
||||
|
||||
In order to build PortAudio with ASIO support, you need to download
|
||||
the ASIO SDK (version 2.0) from Steinberg. Steinberg makes the ASIO
|
||||
SDK available to anyone free of charge, however they do not permit its
|
||||
source code to be distributed.
|
||||
|
||||
NOTE: In some cases the ASIO SDK may require patching, see below
|
||||
for further details.
|
||||
|
||||
http://www.steinberg.de/329+M52087573ab0.html
|
||||
|
||||
If the above link is broken search Google for:
|
||||
"download steinberg ASIO SDK"
|
||||
|
||||
|
||||
|
||||
Building the ASIO SDK on Macintosh
|
||||
----------------------------------
|
||||
|
||||
To build the ASIO SDK on Macintosh you need to compile and link with the
|
||||
following files from the ASIO SDK:
|
||||
|
||||
host/asiodrivers.cpp
|
||||
host/mac/asioshlib.cpp
|
||||
host/mac/codefragements.cpp
|
||||
|
||||
You may also need to adjust your include paths to support inclusion of
|
||||
header files from the above directories.
|
||||
|
||||
|
||||
|
||||
Building the ASIO SDK on Windows
|
||||
--------------------------------
|
||||
|
||||
To build the ASIO SDK on Windows you need to compile and link with the
|
||||
following files from the ASIO SDK:
|
||||
|
||||
asio_sdk\common\asio.cpp
|
||||
asio_sdk\host\asiodrivers.cpp
|
||||
asio_sdk\host\pc\asiolist.cpp
|
||||
|
||||
You may also need to adjust your include paths to support inclusion of
|
||||
header files from the above directories.
|
||||
|
||||
The ASIO SDK depends on the following COM API functions:
|
||||
CoInitialize, CoUninitialize, CoCreateInstance, CLSIDFromString
|
||||
For compilation with MinGW you will need to link with -lole32, for
|
||||
Borland compilers link with Import32.lib.
|
||||
|
||||
|
||||
|
||||
Non-Microsoft (MSVC) Compilers on Windows including Borland and GCC
|
||||
-------------------------------------------------------------------
|
||||
|
||||
Steinberg did not specify a calling convention in the IASIO interface
|
||||
definition. This causes the Microsoft compiler to use the proprietary
|
||||
thiscall convention which is not compatible with other compilers, such
|
||||
as compilers from Borland (BCC and C++Builder) and GNU (gcc).
|
||||
Steinberg's ASIO SDK will compile but crash on initialization if
|
||||
compiled with a non-Microsoft compiler on Windows.
|
||||
|
||||
PortAudio solves this problem using the iasiothiscallresolver library
|
||||
which is included in the distribution. When building ASIO support for
|
||||
non-Microsoft compilers, be sure to compile and link with
|
||||
iasiothiscallresolver.cpp. Note that iasiothiscallresolver includes
|
||||
conditional directives which cause it to have no effect if it is
|
||||
compiled with a Microsoft compiler, or on the Macintosh.
|
||||
|
||||
If you use configure and make (see above), this should be handled
|
||||
automatically for you.
|
||||
|
||||
For further information about the IASIO thiscall problem see this page:
|
||||
http://www.audiomulch.com/~rossb/code/calliasio
|
||||
|
||||
|
||||
|
||||
Macintosh ASIO SDK Bug Patch
|
||||
----------------------------
|
||||
|
||||
There is a bug in the ASIO SDK that causes the Macintosh version to
|
||||
often fail during initialization. Below is a patch that you can apply.
|
||||
|
||||
In codefragments.cpp replace getFrontProcessDirectory function with
|
||||
the following one (GetFrontProcess replaced by GetCurrentProcess).
|
||||
|
||||
|
||||
bool CodeFragments::getFrontProcessDirectory(void *specs)
|
||||
{
|
||||
FSSpec *fss = (FSSpec *)specs;
|
||||
ProcessInfoRec pif;
|
||||
ProcessSerialNumber psn;
|
||||
|
||||
memset(&psn,0,(long)sizeof(ProcessSerialNumber));
|
||||
// if(GetFrontProcess(&psn) == noErr) // wrong !!!
|
||||
if(GetCurrentProcess(&psn) == noErr) // correct !!!
|
||||
{
|
||||
pif.processName = 0;
|
||||
pif.processAppSpec = fss;
|
||||
pif.processInfoLength = sizeof(ProcessInfoRec);
|
||||
if(GetProcessInformation(&psn, &pif) == noErr)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
---
|
||||
BIN
src/hostapi/asio/Callback_adaptation_.pdf
Normal file
BIN
src/hostapi/asio/Callback_adaptation_.pdf
Normal file
Binary file not shown.
BIN
src/hostapi/asio/Pa_ASIO.pdf
Normal file
BIN
src/hostapi/asio/Pa_ASIO.pdf
Normal file
Binary file not shown.
563
src/hostapi/asio/iasiothiscallresolver.cpp
Normal file
563
src/hostapi/asio/iasiothiscallresolver.cpp
Normal file
|
|
@ -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 */
|
||||
|
||||
197
src/hostapi/asio/iasiothiscallresolver.h
Normal file
197
src/hostapi/asio/iasiothiscallresolver.h
Normal file
|
|
@ -0,0 +1,197 @@
|
|||
// ****************************************************************************
|
||||
// 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 */
|
||||
|
||||
|
||||
3965
src/hostapi/asio/pa_asio.cpp
Normal file
3965
src/hostapi/asio/pa_asio.cpp
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue