Various configure changes for MinGW (gps).
[rtaudio-cdist.git] / include / iasiothiscallresolver.cpp
1 /*\r
2         IASIOThiscallResolver.cpp see the comments in iasiothiscallresolver.h for\r
3     the top level description - this comment describes the technical details of\r
4     the implementation.\r
5 \r
6     The latest version of this file is available from:\r
7     http://www.audiomulch.com/~rossb/code/calliasio\r
8 \r
9     please email comments to Ross Bencina <rossb@audiomulch.com>\r
10 \r
11     BACKGROUND\r
12 \r
13     The IASIO interface declared in the Steinberg ASIO 2 SDK declares\r
14     functions with no explicit calling convention. This causes MSVC++ to default\r
15     to using the thiscall convention, which is a proprietary convention not\r
16     implemented by some non-microsoft compilers - notably borland BCC,\r
17     C++Builder, and gcc. MSVC++ is the defacto standard compiler used by\r
18     Steinberg. As a result of this situation, the ASIO sdk will compile with\r
19     any compiler, however attempting to execute the compiled code will cause a\r
20     crash due to different default calling conventions on non-Microsoft\r
21     compilers.\r
22 \r
23     IASIOThiscallResolver solves the problem by providing an adapter class that\r
24     delegates to the IASIO interface using the correct calling convention\r
25     (thiscall). Due to the lack of support for thiscall in the Borland and GCC\r
26     compilers, the calls have been implemented in assembly language.\r
27 \r
28     A number of macros are defined for thiscall function calls with different\r
29     numbers of parameters, with and without return values - it may be possible\r
30     to modify the format of these macros to make them work with other inline\r
31     assemblers.\r
32 \r
33 \r
34     THISCALL DEFINITION\r
35 \r
36     A number of definitions of the thiscall calling convention are floating\r
37     around the internet. The following definition has been validated against\r
38     output from the MSVC++ compiler:\r
39 \r
40     For non-vararg functions, thiscall works as follows: the object (this)\r
41     pointer is passed in ECX. All arguments are passed on the stack in\r
42     right to left order. The return value is placed in EAX. The callee\r
43     clears the passed arguments from the stack.\r
44 \r
45 \r
46     FINDING FUNCTION POINTERS FROM AN IASIO POINTER\r
47 \r
48     The first field of a COM object is a pointer to its vtble. Thus a pointer\r
49     to an object implementing the IASIO interface also points to a pointer to\r
50     that object's vtbl. The vtble is a table of function pointers for all of\r
51     the virtual functions exposed by the implemented interfaces.\r
52 \r
53     If we consider a variable declared as a pointer to IASO:\r
54 \r
55     IASIO *theAsioDriver\r
56 \r
57     theAsioDriver points to:\r
58 \r
59     object implementing IASIO\r
60     {\r
61         IASIOvtbl *vtbl\r
62         other data\r
63     }\r
64 \r
65     in other words, theAsioDriver points to a pointer to an IASIOvtbl\r
66 \r
67     vtbl points to a table of function pointers:\r
68 \r
69     IASIOvtbl ( interface IASIO : public IUnknown )\r
70     {\r
71     (IUnknown functions)\r
72     0   virtual HRESULT STDMETHODCALLTYPE (*QueryInterface)(REFIID riid, void **ppv) = 0;\r
73     4   virtual ULONG STDMETHODCALLTYPE (*AddRef)() = 0;\r
74     8   virtual ULONG STDMETHODCALLTYPE (*Release)() = 0;      \r
75 \r
76     (IASIO functions)\r
77     12  virtual ASIOBool (*init)(void *sysHandle) = 0;\r
78     16  virtual void (*getDriverName)(char *name) = 0;\r
79     20  virtual long (*getDriverVersion)() = 0;\r
80     24  virtual void (*getErrorMessage)(char *string) = 0;\r
81     28  virtual ASIOError (*start)() = 0;\r
82     32  virtual ASIOError (*stop)() = 0;\r
83     36  virtual ASIOError (*getChannels)(long *numInputChannels, long *numOutputChannels) = 0;\r
84     40  virtual ASIOError (*getLatencies)(long *inputLatency, long *outputLatency) = 0;\r
85     44  virtual ASIOError (*getBufferSize)(long *minSize, long *maxSize,\r
86             long *preferredSize, long *granularity) = 0;\r
87     48  virtual ASIOError (*canSampleRate)(ASIOSampleRate sampleRate) = 0;\r
88     52  virtual ASIOError (*getSampleRate)(ASIOSampleRate *sampleRate) = 0;\r
89     56  virtual ASIOError (*setSampleRate)(ASIOSampleRate sampleRate) = 0;\r
90     60  virtual ASIOError (*getClockSources)(ASIOClockSource *clocks, long *numSources) = 0;\r
91     64  virtual ASIOError (*setClockSource)(long reference) = 0;\r
92     68  virtual ASIOError (*getSamplePosition)(ASIOSamples *sPos, ASIOTimeStamp *tStamp) = 0;\r
93     72  virtual ASIOError (*getChannelInfo)(ASIOChannelInfo *info) = 0;\r
94     76  virtual ASIOError (*createBuffers)(ASIOBufferInfo *bufferInfos, long numChannels,\r
95             long bufferSize, ASIOCallbacks *callbacks) = 0;\r
96     80  virtual ASIOError (*disposeBuffers)() = 0;\r
97     84  virtual ASIOError (*controlPanel)() = 0;\r
98     88  virtual ASIOError (*future)(long selector,void *opt) = 0;\r
99     92  virtual ASIOError (*outputReady)() = 0;\r
100     };\r
101 \r
102     The numbers in the left column show the byte offset of each function ptr\r
103     from the beginning of the vtbl. These numbers are used in the code below\r
104     to select different functions.\r
105 \r
106     In order to find the address of a particular function, theAsioDriver\r
107     must first be dereferenced to find the value of the vtbl pointer:\r
108 \r
109     mov     eax, theAsioDriver\r
110     mov     edx, [theAsioDriver]  // edx now points to vtbl[0]\r
111 \r
112     Then an offset must be added to the vtbl pointer to select a\r
113     particular function, for example vtbl+44 points to the slot containing\r
114     a pointer to the getBufferSize function.\r
115 \r
116     Finally vtbl+x must be dereferenced to obtain the value of the function\r
117     pointer stored in that address:\r
118 \r
119     call    [edx+44]    // call the function pointed to by\r
120                         // the value in the getBufferSize field of the vtbl\r
121 \r
122 \r
123     SEE ALSO\r
124 \r
125     Martin Fay's OpenASIO DLL at http://www.martinfay.com solves the same\r
126     problem by providing a new COM interface which wraps IASIO with an\r
127     interface that uses portable calling conventions. OpenASIO must be compiled\r
128     with MSVC, and requires that you ship the OpenASIO DLL with your\r
129     application.\r
130 \r
131     \r
132     ACKNOWLEDGEMENTS\r
133 \r
134     Ross Bencina: worked out the thiscall details above, wrote the original\r
135     Borland asm macros, and a patch for asio.cpp (which is no longer needed).\r
136     Thanks to Martin Fay for introducing me to the issues discussed here,\r
137     and to Rene G. Ceballos for assisting with asm dumps from MSVC++.\r
138 \r
139     Antti Silvast: converted the original calliasio to work with gcc and NASM\r
140     by implementing the asm code in a separate file.\r
141 \r
142         Fraser Adams: modified the original calliasio containing the Borland inline\r
143     asm to add inline asm for gcc i.e. Intel syntax for Borland and AT&T syntax\r
144     for gcc. This seems a neater approach for gcc than to have a separate .asm\r
145     file and it means that we only need one version of the thiscall patch.\r
146 \r
147     Fraser Adams: rewrote the original calliasio patch in the form of the\r
148     IASIOThiscallResolver class in order to avoid modifications to files from\r
149     the Steinberg SDK, which may have had potential licence issues.\r
150 \r
151     Andrew Baldwin: contributed fixes for compatibility problems with more\r
152     recent versions of the gcc assembler.\r
153 */\r
154 \r
155 \r
156 // We only need IASIOThiscallResolver at all if we are on Win32. For other\r
157 // platforms we simply bypass the IASIOThiscallResolver definition to allow us\r
158 // to be safely #include'd whatever the platform to keep client code portable\r
159 #if defined(WIN32) || defined(_WIN32) || defined(__WIN32__)\r
160 \r
161 \r
162 // If microsoft compiler we can call IASIO directly so IASIOThiscallResolver\r
163 // is not used.\r
164 #if !defined(_MSC_VER)\r
165 \r
166 \r
167 #include <new>\r
168 #include <assert.h>\r
169 \r
170 // We have a mechanism in iasiothiscallresolver.h to ensure that asio.h is\r
171 // #include'd before it in client code, we do NOT want to do this test here.\r
172 #define iasiothiscallresolver_sourcefile 1\r
173 #include "iasiothiscallresolver.h"\r
174 #undef iasiothiscallresolver_sourcefile\r
175 \r
176 // iasiothiscallresolver.h redefines ASIOInit for clients, but we don't want\r
177 // this macro defined in this translation unit.\r
178 #undef ASIOInit\r
179 \r
180 \r
181 // theAsioDriver is a global pointer to the current IASIO instance which the\r
182 // ASIO SDK uses to perform all actions on the IASIO interface. We substitute\r
183 // our own forwarding interface into this pointer.\r
184 extern IASIO* theAsioDriver;\r
185 \r
186 \r
187 // The following macros define the inline assembler for BORLAND first then gcc\r
188 \r
189 #if defined(__BCPLUSPLUS__) || defined(__BORLANDC__)          \r
190 \r
191 \r
192 #define CALL_THISCALL_0( resultName, thisPtr, funcOffset )\\r
193     void *this_ = (thisPtr);                                                \\r
194     __asm {                                                                 \\r
195         mov     ecx, this_            ;                                     \\r
196         mov     eax, [ecx]            ;                                     \\r
197         call    [eax+funcOffset]      ;                                     \\r
198         mov     resultName, eax       ;                                     \\r
199     }\r
200 \r
201 \r
202 #define CALL_VOID_THISCALL_1( thisPtr, funcOffset, param1 )\\r
203     void *this_ = (thisPtr);                                                \\r
204     __asm {                                                                 \\r
205         mov     eax, param1           ;                                     \\r
206         push    eax                   ;                                     \\r
207         mov     ecx, this_            ;                                     \\r
208         mov     eax, [ecx]            ;                                     \\r
209         call    [eax+funcOffset]      ;                                     \\r
210     }\r
211 \r
212 \r
213 #define CALL_THISCALL_1( resultName, thisPtr, funcOffset, param1 )\\r
214     void *this_ = (thisPtr);                                                \\r
215     __asm {                                                                 \\r
216         mov     eax, param1           ;                                     \\r
217         push    eax                   ;                                     \\r
218         mov     ecx, this_            ;                                     \\r
219         mov     eax, [ecx]            ;                                     \\r
220         call    [eax+funcOffset]      ;                                     \\r
221         mov     resultName, eax       ;                                     \\r
222     }\r
223 \r
224 \r
225 #define CALL_THISCALL_1_DOUBLE( resultName, thisPtr, funcOffset, param1 )\\r
226     void *this_ = (thisPtr);                                                \\r
227     void *doubleParamPtr_ (&param1);                                        \\r
228     __asm {                                                                 \\r
229         mov     eax, doubleParamPtr_  ;                                     \\r
230         push    [eax+4]               ;                                     \\r
231         push    [eax]                 ;                                     \\r
232         mov     ecx, this_            ;                                     \\r
233         mov     eax, [ecx]            ;                                     \\r
234         call    [eax+funcOffset]      ;                                     \\r
235         mov     resultName, eax       ;                                     \\r
236     }\r
237 \r
238 \r
239 #define CALL_THISCALL_2( resultName, thisPtr, funcOffset, param1, param2 )\\r
240     void *this_ = (thisPtr);                                                \\r
241     __asm {                                                                 \\r
242         mov     eax, param2           ;                                     \\r
243         push    eax                   ;                                     \\r
244         mov     eax, param1           ;                                     \\r
245         push    eax                   ;                                     \\r
246         mov     ecx, this_            ;                                     \\r
247         mov     eax, [ecx]            ;                                     \\r
248         call    [eax+funcOffset]      ;                                     \\r
249         mov     resultName, eax       ;                                     \\r
250     }\r
251 \r
252 \r
253 #define CALL_THISCALL_4( resultName, thisPtr, funcOffset, param1, param2, param3, param4 )\\r
254     void *this_ = (thisPtr);                                                \\r
255     __asm {                                                                 \\r
256         mov     eax, param4           ;                                     \\r
257         push    eax                   ;                                     \\r
258         mov     eax, param3           ;                                     \\r
259         push    eax                   ;                                     \\r
260         mov     eax, param2           ;                                     \\r
261         push    eax                   ;                                     \\r
262         mov     eax, param1           ;                                     \\r
263         push    eax                   ;                                     \\r
264         mov     ecx, this_            ;                                     \\r
265         mov     eax, [ecx]            ;                                     \\r
266         call    [eax+funcOffset]      ;                                     \\r
267         mov     resultName, eax       ;                                     \\r
268     }\r
269 \r
270 \r
271 #elif defined(__GNUC__)\r
272 \r
273 \r
274 #define CALL_THISCALL_0( resultName, thisPtr, funcOffset )                  \\r
275     __asm__ __volatile__ ("movl (%1), %%edx\n\t"                            \\r
276                           "call *"#funcOffset"(%%edx)\n\t"                  \\r
277                           :"=a"(resultName) /* Output Operands */           \\r
278                           :"c"(thisPtr)     /* Input Operands */            \\r
279                          );                                                 \\r
280 \r
281 \r
282 #define CALL_VOID_THISCALL_1( thisPtr, funcOffset, param1 )                 \\r
283     __asm__ __volatile__ ("pushl %0\n\t"                                    \\r
284                           "movl (%1), %%edx\n\t"                            \\r
285                           "call *"#funcOffset"(%%edx)\n\t"                  \\r
286                           :                 /* Output Operands */           \\r
287                           :"r"(param1),     /* Input Operands */            \\r
288                            "c"(thisPtr)                                     \\r
289                          );                                                 \\r
290 \r
291 \r
292 #define CALL_THISCALL_1( resultName, thisPtr, funcOffset, param1 )          \\r
293     __asm__ __volatile__ ("pushl %1\n\t"                                    \\r
294                           "movl (%2), %%edx\n\t"                            \\r
295                           "call *"#funcOffset"(%%edx)\n\t"                  \\r
296                           :"=a"(resultName) /* Output Operands */           \\r
297                           :"r"(param1),     /* Input Operands */            \\r
298                            "c"(thisPtr)                                     \\r
299                           );                                                \\r
300 \r
301 \r
302 #define CALL_THISCALL_1_DOUBLE( resultName, thisPtr, funcOffset, param1 )   \\r
303     __asm__ __volatile__ ("pushl 4(%1)\n\t"                                 \\r
304                           "pushl (%1)\n\t"                                  \\r
305                           "movl (%2), %%edx\n\t"                            \\r
306                           "call *"#funcOffset"(%%edx);\n\t"                 \\r
307                           :"=a"(resultName) /* Output Operands */           \\r
308                           :"a"(&param1),    /* Input Operands */            \\r
309                            /* Note: Using "r" above instead of "a" fails */ \\r
310                            /* when using GCC 3.3.3, and maybe later versions*/\\r
311                            "c"(thisPtr)                                     \\r
312                           );                                                \\r
313 \r
314 \r
315 #define CALL_THISCALL_2( resultName, thisPtr, funcOffset, param1, param2 )  \\r
316     __asm__ __volatile__ ("pushl %1\n\t"                                    \\r
317                           "pushl %2\n\t"                                    \\r
318                           "movl (%3), %%edx\n\t"                            \\r
319                           "call *"#funcOffset"(%%edx)\n\t"                  \\r
320                           :"=a"(resultName) /* Output Operands */           \\r
321                           :"r"(param2),     /* Input Operands */            \\r
322                            "r"(param1),                                     \\r
323                            "c"(thisPtr)                                     \\r
324                           );                                                \\r
325 \r
326 \r
327 #define CALL_THISCALL_4( resultName, thisPtr, funcOffset, param1, param2, param3, param4 )\\r
328     __asm__ __volatile__ ("pushl %1\n\t"                                    \\r
329                           "pushl %2\n\t"                                    \\r
330                           "pushl %3\n\t"                                    \\r
331                           "pushl %4\n\t"                                    \\r
332                           "movl (%5), %%edx\n\t"                            \\r
333                           "call *"#funcOffset"(%%edx)\n\t"                  \\r
334                           :"=a"(resultName) /* Output Operands */           \\r
335                           :"r"(param4),     /* Input Operands  */           \\r
336                            "r"(param3),                                     \\r
337                            "r"(param2),                                     \\r
338                            "r"(param1),                                     \\r
339                            "c"(thisPtr)                                     \\r
340                           );                                                \\r
341 \r
342 #endif\r
343 \r
344 \r
345 \r
346 // Our static singleton instance.\r
347 IASIOThiscallResolver IASIOThiscallResolver::instance;\r
348 \r
349 // Constructor called to initialize static Singleton instance above. Note that\r
350 // it is important not to clear that_ incase it has already been set by the call\r
351 // to placement new in ASIOInit().\r
352 IASIOThiscallResolver::IASIOThiscallResolver()\r
353 {\r
354 }\r
355 \r
356 // Constructor called from ASIOInit() below\r
357 IASIOThiscallResolver::IASIOThiscallResolver(IASIO* that)\r
358 : that_( that )\r
359 {\r
360 }\r
361 \r
362 // Implement IUnknown methods as assert(false). IASIOThiscallResolver is not\r
363 // really a COM object, just a wrapper which will work with the ASIO SDK.\r
364 // If you wanted to use ASIO without the SDK you might want to implement COM\r
365 // aggregation in these methods.\r
366 HRESULT STDMETHODCALLTYPE IASIOThiscallResolver::QueryInterface(REFIID riid, void **ppv)\r
367 {\r
368     (void)riid;     // suppress unused variable warning\r
369 \r
370     assert( false ); // this function should never be called by the ASIO SDK.\r
371 \r
372     *ppv = NULL;\r
373     return E_NOINTERFACE;\r
374 }\r
375 \r
376 ULONG STDMETHODCALLTYPE IASIOThiscallResolver::AddRef()\r
377 {\r
378     assert( false ); // this function should never be called by the ASIO SDK.\r
379 \r
380     return 1;\r
381 }\r
382 \r
383 ULONG STDMETHODCALLTYPE IASIOThiscallResolver::Release()\r
384 {\r
385     assert( false ); // this function should never be called by the ASIO SDK.\r
386     \r
387     return 1;\r
388 }\r
389 \r
390 \r
391 // Implement the IASIO interface methods by performing the vptr manipulation\r
392 // described above then delegating to the real implementation.\r
393 ASIOBool IASIOThiscallResolver::init(void *sysHandle)\r
394 {\r
395     ASIOBool result;\r
396     CALL_THISCALL_1( result, that_, 12, sysHandle );\r
397     return result;\r
398 }\r
399 \r
400 void IASIOThiscallResolver::getDriverName(char *name)\r
401 {\r
402     CALL_VOID_THISCALL_1( that_, 16, name );\r
403 }\r
404 \r
405 long IASIOThiscallResolver::getDriverVersion()\r
406 {\r
407     ASIOBool result;\r
408     CALL_THISCALL_0( result, that_, 20 );\r
409     return result;\r
410 }\r
411 \r
412 void IASIOThiscallResolver::getErrorMessage(char *string)\r
413 {\r
414      CALL_VOID_THISCALL_1( that_, 24, string );\r
415 }\r
416 \r
417 ASIOError IASIOThiscallResolver::start()\r
418 {\r
419     ASIOBool result;\r
420     CALL_THISCALL_0( result, that_, 28 );\r
421     return result;\r
422 }\r
423 \r
424 ASIOError IASIOThiscallResolver::stop()\r
425 {\r
426     ASIOBool result;\r
427     CALL_THISCALL_0( result, that_, 32 );\r
428     return result;\r
429 }\r
430 \r
431 ASIOError IASIOThiscallResolver::getChannels(long *numInputChannels, long *numOutputChannels)\r
432 {\r
433     ASIOBool result;\r
434     CALL_THISCALL_2( result, that_, 36, numInputChannels, numOutputChannels );\r
435     return result;\r
436 }\r
437 \r
438 ASIOError IASIOThiscallResolver::getLatencies(long *inputLatency, long *outputLatency)\r
439 {\r
440     ASIOBool result;\r
441     CALL_THISCALL_2( result, that_, 40, inputLatency, outputLatency );\r
442     return result;\r
443 }\r
444 \r
445 ASIOError IASIOThiscallResolver::getBufferSize(long *minSize, long *maxSize,\r
446         long *preferredSize, long *granularity)\r
447 {\r
448     ASIOBool result;\r
449     CALL_THISCALL_4( result, that_, 44, minSize, maxSize, preferredSize, granularity );\r
450     return result;\r
451 }\r
452 \r
453 ASIOError IASIOThiscallResolver::canSampleRate(ASIOSampleRate sampleRate)\r
454 {\r
455     ASIOBool result;\r
456     CALL_THISCALL_1_DOUBLE( result, that_, 48, sampleRate );\r
457     return result;\r
458 }\r
459 \r
460 ASIOError IASIOThiscallResolver::getSampleRate(ASIOSampleRate *sampleRate)\r
461 {\r
462     ASIOBool result;\r
463     CALL_THISCALL_1( result, that_, 52, sampleRate );\r
464     return result;\r
465 }\r
466 \r
467 ASIOError IASIOThiscallResolver::setSampleRate(ASIOSampleRate sampleRate)\r
468 {    \r
469     ASIOBool result;\r
470     CALL_THISCALL_1_DOUBLE( result, that_, 56, sampleRate );\r
471     return result;\r
472 }\r
473 \r
474 ASIOError IASIOThiscallResolver::getClockSources(ASIOClockSource *clocks, long *numSources)\r
475 {\r
476     ASIOBool result;\r
477     CALL_THISCALL_2( result, that_, 60, clocks, numSources );\r
478     return result;\r
479 }\r
480 \r
481 ASIOError IASIOThiscallResolver::setClockSource(long reference)\r
482 {\r
483     ASIOBool result;\r
484     CALL_THISCALL_1( result, that_, 64, reference );\r
485     return result;\r
486 }\r
487 \r
488 ASIOError IASIOThiscallResolver::getSamplePosition(ASIOSamples *sPos, ASIOTimeStamp *tStamp)\r
489 {\r
490     ASIOBool result;\r
491     CALL_THISCALL_2( result, that_, 68, sPos, tStamp );\r
492     return result;\r
493 }\r
494 \r
495 ASIOError IASIOThiscallResolver::getChannelInfo(ASIOChannelInfo *info)\r
496 {\r
497     ASIOBool result;\r
498     CALL_THISCALL_1( result, that_, 72, info );\r
499     return result;\r
500 }\r
501 \r
502 ASIOError IASIOThiscallResolver::createBuffers(ASIOBufferInfo *bufferInfos,\r
503         long numChannels, long bufferSize, ASIOCallbacks *callbacks)\r
504 {\r
505     ASIOBool result;\r
506     CALL_THISCALL_4( result, that_, 76, bufferInfos, numChannels, bufferSize, callbacks );\r
507     return result;\r
508 }\r
509 \r
510 ASIOError IASIOThiscallResolver::disposeBuffers()\r
511 {\r
512     ASIOBool result;\r
513     CALL_THISCALL_0( result, that_, 80 );\r
514     return result;\r
515 }\r
516 \r
517 ASIOError IASIOThiscallResolver::controlPanel()\r
518 {\r
519     ASIOBool result;\r
520     CALL_THISCALL_0( result, that_, 84 );\r
521     return result;\r
522 }\r
523 \r
524 ASIOError IASIOThiscallResolver::future(long selector,void *opt)\r
525 {\r
526     ASIOBool result;\r
527     CALL_THISCALL_2( result, that_, 88, selector, opt );\r
528     return result;\r
529 }\r
530 \r
531 ASIOError IASIOThiscallResolver::outputReady()\r
532 {\r
533     ASIOBool result;\r
534     CALL_THISCALL_0( result, that_, 92 );\r
535     return result;\r
536 }\r
537 \r
538 \r
539 // Implement our substitute ASIOInit() method\r
540 ASIOError IASIOThiscallResolver::ASIOInit(ASIODriverInfo *info)\r
541 {\r
542     // To ensure that our instance's vptr is correctly constructed, even if\r
543     // ASIOInit is called prior to main(), we explicitly call its constructor\r
544     // (potentially over the top of an existing instance). Note that this is\r
545     // pretty ugly, and is only safe because IASIOThiscallResolver has no\r
546     // destructor and contains no objects with destructors.\r
547     new((void*)&instance) IASIOThiscallResolver( theAsioDriver );\r
548 \r
549     // Interpose between ASIO client code and the real driver.\r
550     theAsioDriver = &instance;\r
551 \r
552     // Note that we never need to switch theAsioDriver back to point to the\r
553     // real driver because theAsioDriver is reset to zero in ASIOExit().\r
554 \r
555     // Delegate to the real ASIOInit\r
556         return ::ASIOInit(info);\r
557 }\r
558 \r
559 \r
560 #endif /* !defined(_MSC_VER) */\r
561 \r
562 #endif /* Win32 */\r
563 \r