1 |
// **************************************************************************** |
2 |
// File: IASIOThiscallResolver.h |
3 |
// Description: The IASIOThiscallResolver class implements the IASIO |
4 |
// interface and acts as a proxy to the real IASIO interface by |
5 |
// calling through its vptr table using the thiscall calling |
6 |
// convention. To put it another way, we interpose |
7 |
// IASIOThiscallResolver between ASIO SDK code and the driver. |
8 |
// This is necessary because most non-Microsoft compilers don't |
9 |
// implement the thiscall calling convention used by IASIO. |
10 |
// |
11 |
// iasiothiscallresolver.cpp contains the background of this |
12 |
// problem plus a technical description of the vptr |
13 |
// manipulations. |
14 |
// |
15 |
// In order to use this mechanism one simply has to add |
16 |
// iasiothiscallresolver.cpp to the list of files to compile |
17 |
// and #include <iasiothiscallresolver.h> |
18 |
// |
19 |
// Note that this #include must come after the other ASIO SDK |
20 |
// #includes, for example: |
21 |
// |
22 |
// #include <windows.h> |
23 |
// #include <asiosys.h> |
24 |
// #include <asio.h> |
25 |
// #include <asiodrivers.h> |
26 |
// #include <iasiothiscallresolver.h> |
27 |
// |
28 |
// Actually the important thing is to #include |
29 |
// <iasiothiscallresolver.h> after <asio.h>. We have |
30 |
// incorporated a test to enforce this ordering. |
31 |
// |
32 |
// The code transparently takes care of the interposition by |
33 |
// using macro substitution to intercept calls to ASIOInit() |
34 |
// and ASIOExit(). We save the original ASIO global |
35 |
// "theAsioDriver" in our "that" variable, and then set |
36 |
// "theAsioDriver" to equal our IASIOThiscallResolver instance. |
37 |
// |
38 |
// Whilst this method of resolving the thiscall problem requires |
39 |
// the addition of #include <iasiothiscallresolver.h> to client |
40 |
// code it has the advantage that it does not break the terms |
41 |
// of the ASIO licence by publishing it. We are NOT modifying |
42 |
// any Steinberg code here, we are merely implementing the IASIO |
43 |
// interface in the same way that we would need to do if we |
44 |
// wished to provide an open source ASIO driver. |
45 |
// |
46 |
// For compilation with MinGW -lole32 needs to be added to the |
47 |
// linker options. For BORLAND, linking with Import32.lib is |
48 |
// sufficient. |
49 |
// |
50 |
// The dependencies are with: CoInitialize, CoUninitialize, |
51 |
// CoCreateInstance, CLSIDFromString - used by asiolist.cpp |
52 |
// and are required on Windows whether ThiscallResolver is used |
53 |
// or not. |
54 |
// |
55 |
// Searching for the above strings in the root library path |
56 |
// of your compiler should enable the correct libraries to be |
57 |
// identified if they aren't immediately obvious. |
58 |
// |
59 |
// Note that the current implementation of IASIOThiscallResolver |
60 |
// is not COM compliant - it does not correctly implement the |
61 |
// IUnknown interface. Implementing it is not necessary because |
62 |
// it is not called by parts of the ASIO SDK which call through |
63 |
// theAsioDriver ptr. The IUnknown methods are implemented as |
64 |
// assert(false) to ensure that the code fails if they are |
65 |
// ever called. |
66 |
// Restrictions: None. Public Domain & Open Source distribute freely |
67 |
// You may use IASIOThiscallResolver commercially as well as |
68 |
// privately. |
69 |
// You the user assume the responsibility for the use of the |
70 |
// files, binary or text, and there is no guarantee or warranty, |
71 |
// expressed or implied, including but not limited to the |
72 |
// implied warranties of merchantability and fitness for a |
73 |
// particular purpose. You assume all responsibility and agree |
74 |
// to hold no entity, copyright holder or distributors liable |
75 |
// for any loss of data or inaccurate representations of data |
76 |
// as a result of using IASIOThiscallResolver. |
77 |
// Version: 1.4 Added separate macro CALL_THISCALL_1_DOUBLE from |
78 |
// Andrew Baldwin, and volatile for whole gcc asm blocks, |
79 |
// both for compatibility with newer gcc versions. Cleaned up |
80 |
// Borland asm to use one less register. |
81 |
// 1.3 Switched to including assert.h for better compatibility. |
82 |
// Wrapped entire .h and .cpp contents with a check for |
83 |
// _MSC_VER to provide better compatibility with MS compilers. |
84 |
// Changed Singleton implementation to use static instance |
85 |
// instead of freestore allocated instance. Removed ASIOExit |
86 |
// macro as it is no longer needed. |
87 |
// 1.2 Removed semicolons from ASIOInit and ASIOExit macros to |
88 |
// allow them to be embedded in expressions (if statements). |
89 |
// Cleaned up some comments. Removed combase.c dependency (it |
90 |
// doesn't compile with BCB anyway) by stubbing IUnknown. |
91 |
// 1.1 Incorporated comments from Ross Bencina including things |
92 |
// such as changing name from ThiscallResolver to |
93 |
// IASIOThiscallResolver, tidying up the constructor, fixing |
94 |
// a bug in IASIOThiscallResolver::ASIOExit() and improving |
95 |
// portability through the use of conditional compilation |
96 |
// 1.0 Initial working version. |
97 |
// Created: 6/09/2003 |
98 |
// Authors: Fraser Adams |
99 |
// Ross Bencina |
100 |
// Rene G. Ceballos |
101 |
// Martin Fay |
102 |
// Antti Silvast |
103 |
// Andrew Baldwin |
104 |
// |
105 |
// **************************************************************************** |
106 |
|
107 |
#ifndef included_iasiothiscallresolver_h |
108 |
#define included_iasiothiscallresolver_h |
109 |
|
110 |
// We only need IASIOThiscallResolver at all if we are on Win32. For other |
111 |
// platforms we simply bypass the IASIOThiscallResolver definition to allow us |
112 |
// to be safely #include'd whatever the platform to keep client code portable |
113 |
#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) |
114 |
|
115 |
// If microsoft compiler we can call IASIO directly so IASIOThiscallResolver |
116 |
// is not used. |
117 |
#if !defined(_MSC_VER) |
118 |
|
119 |
// The following is in order to ensure that this header is only included after |
120 |
// the other ASIO headers (except for the case of iasiothiscallresolver.cpp). |
121 |
// We need to do this because IASIOThiscallResolver works by eclipsing the |
122 |
// original definition of ASIOInit() with a macro (see below). |
123 |
#if !defined(iasiothiscallresolver_sourcefile) |
124 |
#if !defined(__ASIO_H) |
125 |
#error iasiothiscallresolver.h must be included AFTER asio.h |
126 |
#endif |
127 |
#endif |
128 |
|
129 |
#include <windows.h> |
130 |
#include <asiodrvr.h> /* From ASIO SDK */ |
131 |
|
132 |
class IASIOThiscallResolver : public IASIO { |
133 |
private: |
134 |
IASIO* that_; // Points to the real IASIO |
135 |
|
136 |
static IASIOThiscallResolver instance; // Singleton instance |
137 |
|
138 |
// Constructors - declared private so construction is limited to |
139 |
// our Singleton instance |
140 |
IASIOThiscallResolver(); |
141 |
IASIOThiscallResolver(IASIO* that); |
142 |
|
143 |
public: |
144 |
// Methods from the IUnknown interface. We don't fully implement IUnknown |
145 |
// because the ASIO SDK never calls these methods through theAsioDriver ptr. |
146 |
// These methods are implemented as assert(false). |
147 |
virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppv); |
148 |
virtual ULONG STDMETHODCALLTYPE AddRef(); |
149 |
virtual ULONG STDMETHODCALLTYPE Release(); |
150 |
|
151 |
// Methods from the IASIO interface, implemented as forwarning calls to that. |
152 |
virtual ASIOBool init(void *sysHandle); |
153 |
virtual void getDriverName(char *name); |
154 |
virtual long getDriverVersion(); |
155 |
virtual void getErrorMessage(char *string); |
156 |
virtual ASIOError start(); |
157 |
virtual ASIOError stop(); |
158 |
virtual ASIOError getChannels(long *numInputChannels, long *numOutputChannels); |
159 |
virtual ASIOError getLatencies(long *inputLatency, long *outputLatency); |
160 |
virtual ASIOError getBufferSize(long *minSize, long *maxSize, long *preferredSize, long *granularity); |
161 |
virtual ASIOError canSampleRate(ASIOSampleRate sampleRate); |
162 |
virtual ASIOError getSampleRate(ASIOSampleRate *sampleRate); |
163 |
virtual ASIOError setSampleRate(ASIOSampleRate sampleRate); |
164 |
virtual ASIOError getClockSources(ASIOClockSource *clocks, long *numSources); |
165 |
virtual ASIOError setClockSource(long reference); |
166 |
virtual ASIOError getSamplePosition(ASIOSamples *sPos, ASIOTimeStamp *tStamp); |
167 |
virtual ASIOError getChannelInfo(ASIOChannelInfo *info); |
168 |
virtual ASIOError createBuffers(ASIOBufferInfo *bufferInfos, long numChannels, long bufferSize, ASIOCallbacks *callbacks); |
169 |
virtual ASIOError disposeBuffers(); |
170 |
virtual ASIOError controlPanel(); |
171 |
virtual ASIOError future(long selector,void *opt); |
172 |
virtual ASIOError outputReady(); |
173 |
|
174 |
// Class method, see ASIOInit() macro below. |
175 |
static ASIOError ASIOInit(ASIODriverInfo *info); // Delegates to ::ASIOInit |
176 |
}; |
177 |
|
178 |
// Replace calls to ASIOInit with our interposing version. |
179 |
// This macro enables us to perform thiscall resolution simply by #including |
180 |
// <iasiothiscallresolver.h> after the asio #includes (this file _must_ be |
181 |
// included _after_ the asio #includes) |
182 |
|
183 |
#define ASIOInit(name) IASIOThiscallResolver::ASIOInit((name)) |
184 |
|
185 |
#endif /* !defined(_MSC_VER) */ |
186 |
|
187 |
#endif /* Win32 */ |
188 |
|
189 |
#endif /* included_iasiothiscallresolver_h */ |