To check out this repository please hg clone the following URL, or open the URL using EasyMercurial or your preferred Mercurial client.

The primary repository for this project is hosted at https://github.com/sonic-visualiser/sv-dependency-builds .
This repository is a read-only copy which is updated automatically every hour.

Statistics Download as Zip
| Branch: | Tag: | Revision:

root / src / portaudio_20161030_catalina_patch / src / hostapi / asio / iasiothiscallresolver.cpp @ 162:d43aab368df9

History | View | Annotate | Download (23.4 KB)

1
/*
2
        IASIOThiscallResolver.cpp see the comments in iasiothiscallresolver.h for
3
    the top level description - this comment describes the technical details of
4
    the implementation.
5

6
    The latest version of this file is available from:
7
    http://www.audiomulch.com/~rossb/code/calliasio
8

9
    please email comments to Ross Bencina <rossb@audiomulch.com>
10

11
    BACKGROUND
12

13
    The IASIO interface declared in the Steinberg ASIO 2 SDK declares
14
    functions with no explicit calling convention. This causes MSVC++ to default
15
    to using the thiscall convention, which is a proprietary convention not
16
    implemented by some non-microsoft compilers - notably borland BCC,
17
    C++Builder, and gcc. MSVC++ is the defacto standard compiler used by
18
    Steinberg. As a result of this situation, the ASIO sdk will compile with
19
    any compiler, however attempting to execute the compiled code will cause a
20
    crash due to different default calling conventions on non-Microsoft
21
    compilers.
22

23
    IASIOThiscallResolver solves the problem by providing an adapter class that
24
    delegates to the IASIO interface using the correct calling convention
25
    (thiscall). Due to the lack of support for thiscall in the Borland and GCC
26
    compilers, the calls have been implemented in assembly language.
27

28
    A number of macros are defined for thiscall function calls with different
29
    numbers of parameters, with and without return values - it may be possible
30
    to modify the format of these macros to make them work with other inline
31
    assemblers.
32

33

34
    THISCALL DEFINITION
35

36
    A number of definitions of the thiscall calling convention are floating
37
    around the internet. The following definition has been validated against
38
    output from the MSVC++ compiler:
39

40
    For non-vararg functions, thiscall works as follows: the object (this)
41
    pointer is passed in ECX. All arguments are passed on the stack in
42
    right to left order. The return value is placed in EAX. The callee
43
    clears the passed arguments from the stack.
44

45

46
    FINDING FUNCTION POINTERS FROM AN IASIO POINTER
47

48
    The first field of a COM object is a pointer to its vtble. Thus a pointer
49
    to an object implementing the IASIO interface also points to a pointer to
50
    that object's vtbl. The vtble is a table of function pointers for all of
51
    the virtual functions exposed by the implemented interfaces.
52

53
    If we consider a variable declared as a pointer to IASO:
54

55
    IASIO *theAsioDriver
56

57
    theAsioDriver points to:
58

59
    object implementing IASIO
60
    {
61
        IASIOvtbl *vtbl
62
        other data
63
    }
64

65
    in other words, theAsioDriver points to a pointer to an IASIOvtbl
66

67
    vtbl points to a table of function pointers:
68

69
    IASIOvtbl ( interface IASIO : public IUnknown )
70
    {
71
    (IUnknown functions)
72
    0   virtual HRESULT STDMETHODCALLTYPE (*QueryInterface)(REFIID riid, void **ppv) = 0;
73
    4   virtual ULONG STDMETHODCALLTYPE (*AddRef)() = 0;
74
    8   virtual ULONG STDMETHODCALLTYPE (*Release)() = 0;      
75

76
    (IASIO functions)
77
    12        virtual ASIOBool (*init)(void *sysHandle) = 0;
78
    16        virtual void (*getDriverName)(char *name) = 0;
79
    20        virtual long (*getDriverVersion)() = 0;
80
    24        virtual void (*getErrorMessage)(char *string) = 0;
81
    28        virtual ASIOError (*start)() = 0;
82
    32        virtual ASIOError (*stop)() = 0;
83
    36        virtual ASIOError (*getChannels)(long *numInputChannels, long *numOutputChannels) = 0;
84
    40        virtual ASIOError (*getLatencies)(long *inputLatency, long *outputLatency) = 0;
85
    44        virtual ASIOError (*getBufferSize)(long *minSize, long *maxSize,
86
            long *preferredSize, long *granularity) = 0;
87
    48        virtual ASIOError (*canSampleRate)(ASIOSampleRate sampleRate) = 0;
88
    52        virtual ASIOError (*getSampleRate)(ASIOSampleRate *sampleRate) = 0;
89
    56        virtual ASIOError (*setSampleRate)(ASIOSampleRate sampleRate) = 0;
90
    60        virtual ASIOError (*getClockSources)(ASIOClockSource *clocks, long *numSources) = 0;
91
    64        virtual ASIOError (*setClockSource)(long reference) = 0;
92
    68        virtual ASIOError (*getSamplePosition)(ASIOSamples *sPos, ASIOTimeStamp *tStamp) = 0;
93
    72        virtual ASIOError (*getChannelInfo)(ASIOChannelInfo *info) = 0;
94
    76        virtual ASIOError (*createBuffers)(ASIOBufferInfo *bufferInfos, long numChannels,
95
            long bufferSize, ASIOCallbacks *callbacks) = 0;
96
    80        virtual ASIOError (*disposeBuffers)() = 0;
97
    84        virtual ASIOError (*controlPanel)() = 0;
98
    88        virtual ASIOError (*future)(long selector,void *opt) = 0;
99
    92        virtual ASIOError (*outputReady)() = 0;
100
    };
101

102
    The numbers in the left column show the byte offset of each function ptr
103
    from the beginning of the vtbl. These numbers are used in the code below
104
    to select different functions.
105

106
    In order to find the address of a particular function, theAsioDriver
107
    must first be dereferenced to find the value of the vtbl pointer:
108

109
    mov     eax, theAsioDriver
110
    mov     edx, [theAsioDriver]  // edx now points to vtbl[0]
111

112
    Then an offset must be added to the vtbl pointer to select a
113
    particular function, for example vtbl+44 points to the slot containing
114
    a pointer to the getBufferSize function.
115

116
    Finally vtbl+x must be dereferenced to obtain the value of the function
117
    pointer stored in that address:
118

119
    call    [edx+44]    // call the function pointed to by
120
                        // the value in the getBufferSize field of the vtbl
121

122

123
    SEE ALSO
124

125
    Martin Fay's OpenASIO DLL at http://www.martinfay.com solves the same
126
    problem by providing a new COM interface which wraps IASIO with an
127
    interface that uses portable calling conventions. OpenASIO must be compiled
128
    with MSVC, and requires that you ship the OpenASIO DLL with your
129
    application.
130

131
    
132
    ACKNOWLEDGEMENTS
133

134
    Ross Bencina: worked out the thiscall details above, wrote the original
135
    Borland asm macros, and a patch for asio.cpp (which is no longer needed).
136
    Thanks to Martin Fay for introducing me to the issues discussed here,
137
    and to Rene G. Ceballos for assisting with asm dumps from MSVC++.
138

139
    Antti Silvast: converted the original calliasio to work with gcc and NASM
140
    by implementing the asm code in a separate file.
141

142
        Fraser Adams: modified the original calliasio containing the Borland inline
143
    asm to add inline asm for gcc i.e. Intel syntax for Borland and AT&T syntax
144
    for gcc. This seems a neater approach for gcc than to have a separate .asm
145
    file and it means that we only need one version of the thiscall patch.
146

147
    Fraser Adams: rewrote the original calliasio patch in the form of the
148
    IASIOThiscallResolver class in order to avoid modifications to files from
149
    the Steinberg SDK, which may have had potential licence issues.
150

151
    Andrew Baldwin: contributed fixes for compatibility problems with more
152
    recent versions of the gcc assembler.
153
*/
154

    
155

    
156
// We only need IASIOThiscallResolver at all if we are on Win32. For other
157
// platforms we simply bypass the IASIOThiscallResolver definition to allow us
158
// to be safely #include'd whatever the platform to keep client code portable
159
#if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__)) && !defined(_WIN64)
160

    
161

    
162
// If microsoft compiler we can call IASIO directly so IASIOThiscallResolver
163
// is not used.
164
#if !defined(_MSC_VER)
165

    
166

    
167
#include <new>
168
#include <assert.h>
169

    
170
// We have a mechanism in iasiothiscallresolver.h to ensure that asio.h is
171
// #include'd before it in client code, we do NOT want to do this test here.
172
#define iasiothiscallresolver_sourcefile 1
173
#include "iasiothiscallresolver.h"
174
#undef iasiothiscallresolver_sourcefile
175

    
176
// iasiothiscallresolver.h redefines ASIOInit for clients, but we don't want
177
// this macro defined in this translation unit.
178
#undef ASIOInit
179

    
180

    
181
// theAsioDriver is a global pointer to the current IASIO instance which the
182
// ASIO SDK uses to perform all actions on the IASIO interface. We substitute
183
// our own forwarding interface into this pointer.
184
extern IASIO* theAsioDriver;
185

    
186

    
187
// The following macros define the inline assembler for BORLAND first then gcc
188

    
189
#if defined(__BCPLUSPLUS__) || defined(__BORLANDC__)          
190

    
191

    
192
#define CALL_THISCALL_0( resultName, thisPtr, funcOffset )\
193
    void *this_ = (thisPtr);                                                \
194
    __asm {                                                                 \
195
        mov     ecx, this_            ;                                     \
196
        mov     eax, [ecx]            ;                                     \
197
        call    [eax+funcOffset]      ;                                     \
198
        mov     resultName, eax       ;                                     \
199
    }
200

    
201

    
202
#define CALL_VOID_THISCALL_1( thisPtr, funcOffset, param1 )\
203
    void *this_ = (thisPtr);                                                \
204
    __asm {                                                                 \
205
        mov     eax, param1           ;                                     \
206
        push    eax                   ;                                     \
207
        mov     ecx, this_            ;                                     \
208
        mov     eax, [ecx]            ;                                     \
209
        call    [eax+funcOffset]      ;                                     \
210
    }
211

    
212

    
213
#define CALL_THISCALL_1( resultName, thisPtr, funcOffset, param1 )\
214
    void *this_ = (thisPtr);                                                \
215
    __asm {                                                                 \
216
        mov     eax, param1           ;                                     \
217
        push    eax                   ;                                     \
218
        mov     ecx, this_            ;                                     \
219
        mov     eax, [ecx]            ;                                     \
220
        call    [eax+funcOffset]      ;                                     \
221
        mov     resultName, eax       ;                                     \
222
    }
223

    
224

    
225
#define CALL_THISCALL_1_DOUBLE( resultName, thisPtr, funcOffset, param1 )\
226
    void *this_ = (thisPtr);                                                \
227
    void *doubleParamPtr_ (&param1);                                        \
228
    __asm {                                                                 \
229
        mov     eax, doubleParamPtr_  ;                                     \
230
        push    [eax+4]               ;                                     \
231
        push    [eax]                 ;                                     \
232
        mov     ecx, this_            ;                                     \
233
        mov     eax, [ecx]            ;                                     \
234
        call    [eax+funcOffset]      ;                                     \
235
        mov     resultName, eax       ;                                     \
236
    }
237

    
238

    
239
#define CALL_THISCALL_2( resultName, thisPtr, funcOffset, param1, param2 )\
240
    void *this_ = (thisPtr);                                                \
241
    __asm {                                                                 \
242
        mov     eax, param2           ;                                     \
243
        push    eax                   ;                                     \
244
        mov     eax, param1           ;                                     \
245
        push    eax                   ;                                     \
246
        mov     ecx, this_            ;                                     \
247
        mov     eax, [ecx]            ;                                     \
248
        call    [eax+funcOffset]      ;                                     \
249
        mov     resultName, eax       ;                                     \
250
    }
251

    
252

    
253
#define CALL_THISCALL_4( resultName, thisPtr, funcOffset, param1, param2, param3, param4 )\
254
    void *this_ = (thisPtr);                                                \
255
    __asm {                                                                 \
256
        mov     eax, param4           ;                                     \
257
        push    eax                   ;                                     \
258
        mov     eax, param3           ;                                     \
259
        push    eax                   ;                                     \
260
        mov     eax, param2           ;                                     \
261
        push    eax                   ;                                     \
262
        mov     eax, param1           ;                                     \
263
        push    eax                   ;                                     \
264
        mov     ecx, this_            ;                                     \
265
        mov     eax, [ecx]            ;                                     \
266
        call    [eax+funcOffset]      ;                                     \
267
        mov     resultName, eax       ;                                     \
268
    }
269

    
270

    
271
#elif defined(__GNUC__)
272

    
273

    
274
#define CALL_THISCALL_0( resultName, thisPtr, funcOffset )                  \
275
    __asm__ __volatile__ ("movl (%1), %%edx\n\t"                            \
276
                          "call *"#funcOffset"(%%edx)\n\t"                  \
277
                          :"=a"(resultName) /* Output Operands */           \
278
                          :"c"(thisPtr)     /* Input Operands */            \
279
                          : "%edx" /* Clobbered Registers */                \
280
                         );                                                 \
281

    
282

    
283
#define CALL_VOID_THISCALL_1( thisPtr, funcOffset, param1 )                 \
284
    __asm__ __volatile__ ("pushl %0\n\t"                                    \
285
                          "movl (%1), %%edx\n\t"                            \
286
                          "call *"#funcOffset"(%%edx)\n\t"                  \
287
                          :                 /* Output Operands */           \
288
                          :"r"(param1),     /* Input Operands */            \
289
                           "c"(thisPtr)                                     \
290
                          : "%edx" /* Clobbered Registers */                \
291
                         );                                                 \
292

    
293

    
294
#define CALL_THISCALL_1( resultName, thisPtr, funcOffset, param1 )          \
295
    __asm__ __volatile__ ("pushl %1\n\t"                                    \
296
                          "movl (%2), %%edx\n\t"                            \
297
                          "call *"#funcOffset"(%%edx)\n\t"                  \
298
                          :"=a"(resultName) /* Output Operands */           \
299
                          :"r"(param1),     /* Input Operands */            \
300
                           "c"(thisPtr)                                     \
301
                          : "%edx" /* Clobbered Registers */                \
302
                          );                                                \
303

    
304

    
305
#define CALL_THISCALL_1_DOUBLE( resultName, thisPtr, funcOffset, param1 )   \
306
    do {                                                                    \
307
    double param1f64 = param1; /* Cast explicitly to double */              \
308
    double *param1f64Ptr = &param1f64; /* Make pointer to address */        \
309
     __asm__ __volatile__ ("pushl 4(%1)\n\t"                                \
310
                           "pushl (%1)\n\t"                                 \
311
                           "movl (%2), %%edx\n\t"                           \
312
                           "call *"#funcOffset"(%%edx);\n\t"                \
313
                           : "=a"(resultName) /* Output Operands */         \
314
                           : "r"(param1f64Ptr),  /* Input Operands */       \
315
                           "c"(thisPtr),                                    \
316
                           "m"(*param1f64Ptr) /* Using address */           \
317
                           : "%edx" /* Clobbered Registers */               \
318
                           );                                               \
319
    } while (0);                                                            \
320

    
321

    
322
#define CALL_THISCALL_2( resultName, thisPtr, funcOffset, param1, param2 )  \
323
    __asm__ __volatile__ ("pushl %1\n\t"                                    \
324
                          "pushl %2\n\t"                                    \
325
                          "movl (%3), %%edx\n\t"                            \
326
                          "call *"#funcOffset"(%%edx)\n\t"                  \
327
                          :"=a"(resultName) /* Output Operands */           \
328
                          :"r"(param2),     /* Input Operands */            \
329
                           "r"(param1),                                     \
330
                           "c"(thisPtr)                                     \
331
                          : "%edx" /* Clobbered Registers */                \
332
                          );                                                \
333

    
334

    
335
#define CALL_THISCALL_4( resultName, thisPtr, funcOffset, param1, param2, param3, param4 )\
336
    __asm__ __volatile__ ("pushl %1\n\t"                                    \
337
                          "pushl %2\n\t"                                    \
338
                          "pushl %3\n\t"                                    \
339
                          "pushl %4\n\t"                                    \
340
                          "movl (%5), %%edx\n\t"                            \
341
                          "call *"#funcOffset"(%%edx)\n\t"                  \
342
                          :"=a"(resultName) /* Output Operands */           \
343
                          :"r"(param4),     /* Input Operands  */           \
344
                           "r"(param3),                                     \
345
                           "r"(param2),                                     \
346
                           "r"(param1),                                     \
347
                           "c"(thisPtr)                                     \
348
                          : "%edx" /* Clobbered Registers */                \
349
                          );                                                \
350

    
351
#endif
352

    
353

    
354

    
355
// Our static singleton instance.
356
IASIOThiscallResolver IASIOThiscallResolver::instance;
357

    
358
// Constructor called to initialize static Singleton instance above. Note that
359
// it is important not to clear that_ incase it has already been set by the call
360
// to placement new in ASIOInit().
361
IASIOThiscallResolver::IASIOThiscallResolver()
362
{
363
}
364

    
365
// Constructor called from ASIOInit() below
366
IASIOThiscallResolver::IASIOThiscallResolver(IASIO* that)
367
: that_( that )
368
{
369
}
370

    
371
// Implement IUnknown methods as assert(false). IASIOThiscallResolver is not
372
// really a COM object, just a wrapper which will work with the ASIO SDK.
373
// If you wanted to use ASIO without the SDK you might want to implement COM
374
// aggregation in these methods.
375
HRESULT STDMETHODCALLTYPE IASIOThiscallResolver::QueryInterface(REFIID riid, void **ppv)
376
{
377
    (void)riid;     // suppress unused variable warning
378

    
379
    assert( false ); // this function should never be called by the ASIO SDK.
380

    
381
    *ppv = NULL;
382
    return E_NOINTERFACE;
383
}
384

    
385
ULONG STDMETHODCALLTYPE IASIOThiscallResolver::AddRef()
386
{
387
    assert( false ); // this function should never be called by the ASIO SDK.
388

    
389
    return 1;
390
}
391

    
392
ULONG STDMETHODCALLTYPE IASIOThiscallResolver::Release()
393
{
394
    assert( false ); // this function should never be called by the ASIO SDK.
395
    
396
    return 1;
397
}
398

    
399

    
400
// Implement the IASIO interface methods by performing the vptr manipulation
401
// described above then delegating to the real implementation.
402
ASIOBool IASIOThiscallResolver::init(void *sysHandle)
403
{
404
    ASIOBool result;
405
    CALL_THISCALL_1( result, that_, 12, sysHandle );
406
    return result;
407
}
408

    
409
void IASIOThiscallResolver::getDriverName(char *name)
410
{
411
    CALL_VOID_THISCALL_1( that_, 16, name );
412
}
413

    
414
long IASIOThiscallResolver::getDriverVersion()
415
{
416
    ASIOBool result;
417
    CALL_THISCALL_0( result, that_, 20 );
418
    return result;
419
}
420

    
421
void IASIOThiscallResolver::getErrorMessage(char *string)
422
{
423
     CALL_VOID_THISCALL_1( that_, 24, string );
424
}
425

    
426
ASIOError IASIOThiscallResolver::start()
427
{
428
    ASIOBool result;
429
    CALL_THISCALL_0( result, that_, 28 );
430
    return result;
431
}
432

    
433
ASIOError IASIOThiscallResolver::stop()
434
{
435
    ASIOBool result;
436
    CALL_THISCALL_0( result, that_, 32 );
437
    return result;
438
}
439

    
440
ASIOError IASIOThiscallResolver::getChannels(long *numInputChannels, long *numOutputChannels)
441
{
442
    ASIOBool result;
443
    CALL_THISCALL_2( result, that_, 36, numInputChannels, numOutputChannels );
444
    return result;
445
}
446

    
447
ASIOError IASIOThiscallResolver::getLatencies(long *inputLatency, long *outputLatency)
448
{
449
    ASIOBool result;
450
    CALL_THISCALL_2( result, that_, 40, inputLatency, outputLatency );
451
    return result;
452
}
453

    
454
ASIOError IASIOThiscallResolver::getBufferSize(long *minSize, long *maxSize,
455
        long *preferredSize, long *granularity)
456
{
457
    ASIOBool result;
458
    CALL_THISCALL_4( result, that_, 44, minSize, maxSize, preferredSize, granularity );
459
    return result;
460
}
461

    
462
ASIOError IASIOThiscallResolver::canSampleRate(ASIOSampleRate sampleRate)
463
{
464
    ASIOBool result;
465
    CALL_THISCALL_1_DOUBLE( result, that_, 48, sampleRate );
466
    return result;
467
}
468

    
469
ASIOError IASIOThiscallResolver::getSampleRate(ASIOSampleRate *sampleRate)
470
{
471
    ASIOBool result;
472
    CALL_THISCALL_1( result, that_, 52, sampleRate );
473
    return result;
474
}
475

    
476
ASIOError IASIOThiscallResolver::setSampleRate(ASIOSampleRate sampleRate)
477
{    
478
    ASIOBool result;
479
    CALL_THISCALL_1_DOUBLE( result, that_, 56, sampleRate );
480
    return result;
481
}
482

    
483
ASIOError IASIOThiscallResolver::getClockSources(ASIOClockSource *clocks, long *numSources)
484
{
485
    ASIOBool result;
486
    CALL_THISCALL_2( result, that_, 60, clocks, numSources );
487
    return result;
488
}
489

    
490
ASIOError IASIOThiscallResolver::setClockSource(long reference)
491
{
492
    ASIOBool result;
493
    CALL_THISCALL_1( result, that_, 64, reference );
494
    return result;
495
}
496

    
497
ASIOError IASIOThiscallResolver::getSamplePosition(ASIOSamples *sPos, ASIOTimeStamp *tStamp)
498
{
499
    ASIOBool result;
500
    CALL_THISCALL_2( result, that_, 68, sPos, tStamp );
501
    return result;
502
}
503

    
504
ASIOError IASIOThiscallResolver::getChannelInfo(ASIOChannelInfo *info)
505
{
506
    ASIOBool result;
507
    CALL_THISCALL_1( result, that_, 72, info );
508
    return result;
509
}
510

    
511
ASIOError IASIOThiscallResolver::createBuffers(ASIOBufferInfo *bufferInfos,
512
        long numChannels, long bufferSize, ASIOCallbacks *callbacks)
513
{
514
    ASIOBool result;
515
    CALL_THISCALL_4( result, that_, 76, bufferInfos, numChannels, bufferSize, callbacks );
516
    return result;
517
}
518

    
519
ASIOError IASIOThiscallResolver::disposeBuffers()
520
{
521
    ASIOBool result;
522
    CALL_THISCALL_0( result, that_, 80 );
523
    return result;
524
}
525

    
526
ASIOError IASIOThiscallResolver::controlPanel()
527
{
528
    ASIOBool result;
529
    CALL_THISCALL_0( result, that_, 84 );
530
    return result;
531
}
532

    
533
ASIOError IASIOThiscallResolver::future(long selector,void *opt)
534
{
535
    ASIOBool result;
536
    CALL_THISCALL_2( result, that_, 88, selector, opt );
537
    return result;
538
}
539

    
540
ASIOError IASIOThiscallResolver::outputReady()
541
{
542
    ASIOBool result;
543
    CALL_THISCALL_0( result, that_, 92 );
544
    return result;
545
}
546

    
547

    
548
// Implement our substitute ASIOInit() method
549
ASIOError IASIOThiscallResolver::ASIOInit(ASIODriverInfo *info)
550
{
551
    // To ensure that our instance's vptr is correctly constructed, even if
552
    // ASIOInit is called prior to main(), we explicitly call its constructor
553
    // (potentially over the top of an existing instance). Note that this is
554
    // pretty ugly, and is only safe because IASIOThiscallResolver has no
555
    // destructor and contains no objects with destructors.
556
    new((void*)&instance) IASIOThiscallResolver( theAsioDriver );
557

    
558
    // Interpose between ASIO client code and the real driver.
559
    theAsioDriver = &instance;
560

    
561
    // Note that we never need to switch theAsioDriver back to point to the
562
    // real driver because theAsioDriver is reset to zero in ASIOExit().
563

    
564
    // Delegate to the real ASIOInit
565
        return ::ASIOInit(info);
566
}
567

    
568

    
569
#endif /* !defined(_MSC_VER) */
570

    
571
#endif /* Win32 */
572