2
* $Id: pa_asio.cpp 1416 2009-06-16 16:12:41Z rossb $
3
* Portable Audio I/O Library for ASIO Drivers
5
* Author: Stephane Letz
6
* Based on the Open Source API proposed by Ross Bencina
7
* Copyright (c) 2000-2002 Stephane Letz, Phil Burk, Ross Bencina
8
* Blocking i/o implementation by Sven Fischer, Institute of Hearing
9
* Technology and Audiology (www.hoertechnik-audiologie.de)
11
* Permission is hereby granted, free of charge, to any person obtaining
12
* a copy of this software and associated documentation files
13
* (the "Software"), to deal in the Software without restriction,
14
* including without limitation the rights to use, copy, modify, merge,
15
* publish, distribute, sublicense, and/or sell copies of the Software,
16
* and to permit persons to whom the Software is furnished to do so,
17
* subject to the following conditions:
19
* The above copyright notice and this permission notice shall be
20
* included in all copies or substantial portions of the Software.
22
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
25
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
26
* ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
27
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
28
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
32
* The text above constitutes the entire PortAudio license; however,
33
* the PortAudio community also makes the following non-binding requests:
35
* Any person wishing to distribute modifications to the Software is
36
* requested to send the modifications to the original developer so that
37
* they can be incorporated into the canonical version. It is also
38
* requested that these non-binding requests be included along with the
42
/* Modification History
44
08-03-01 First version : Stephane Letz
45
08-06-01 Tweaks for PC, use C++, buffer allocation, Float32 to Int32 conversion : Phil Burk
46
08-20-01 More conversion, PA_StreamTime, Pa_GetHostError : Stephane Letz
47
08-21-01 PaUInt8 bug correction, implementation of ASIOSTFloat32LSB and ASIOSTFloat32MSB native formats : Stephane Letz
48
08-24-01 MAX_INT32_FP hack, another Uint8 fix : Stephane and Phil
49
08-27-01 Implementation of hostBufferSize < userBufferSize case, better management of the ouput buffer when
50
the stream is stopped : Stephane Letz
51
08-28-01 Check the stream pointer for null in bufferSwitchTimeInfo, correct bug in bufferSwitchTimeInfo when
52
the stream is stopped : Stephane Letz
53
10-12-01 Correct the PaHost_CalcNumHostBuffers function: computes FramesPerHostBuffer to be the lowest that
54
respect requested FramesPerUserBuffer and userBuffersPerHostBuffer : Stephane Letz
55
10-26-01 Management of hostBufferSize and userBufferSize of any size : Stephane Letz
56
10-27-01 Improve calculus of hostBufferSize to be multiple or divisor of userBufferSize if possible : Stephane and Phil
57
10-29-01 Change MAX_INT32_FP to (2147483520.0f) to prevent roundup to 0x80000000 : Phil Burk
58
10-31-01 Clear the ouput buffer and user buffers in PaHost_StartOutput, correct bug in GetFirstMultiple : Stephane Letz
59
11-06-01 Rename functions : Stephane Letz
60
11-08-01 New Pa_ASIO_Adaptor_Init function to init Callback adpatation variables, cleanup of Pa_ASIO_Callback_Input: Stephane Letz
61
11-29-01 Break apart device loading to debug random failure in Pa_ASIO_QueryDeviceInfo ; Phil Burk
62
01-03-02 Desallocate all resources in PaHost_Term for cases where Pa_CloseStream is not called properly : Stephane Letz
63
02-01-02 Cleanup, test of multiple-stream opening : Stephane Letz
64
19-02-02 New Pa_ASIO_loadDriver that calls CoInitialize on each thread on Windows : Stephane Letz
65
09-04-02 Correct error code management in PaHost_Term, removes various compiler warning : Stephane Letz
66
12-04-02 Add Mac includes for <Devices.h> and <Timer.h> : Phil Burk
67
13-04-02 Removes another compiler warning : Stephane Letz
68
30-04-02 Pa_ASIO_QueryDeviceInfo bug correction, memory allocation checking, better error handling : D Viens, P Burk, S Letz
69
12-06-02 Rehashed into new multi-api infrastructure, added support for all ASIO sample formats : Ross Bencina
70
18-06-02 Added pa_asio.h, PaAsio_GetAvailableLatencyValues() : Ross B.
71
21-06-02 Added SelectHostBufferSize() which selects host buffer size based on user latency parameters : Ross Bencina
72
** NOTE maintanance history is now stored in CVS **
78
Note that specific support for paInputUnderflow, paOutputOverflow and
79
paNeverDropInput is not necessary or possible with this driver due to the
80
synchronous full duplex double-buffered architecture of ASIO.
82
@todo implement host api specific extension to set i/o buffer sizes in frames
84
@todo review ReadStream, WriteStream, GetStreamReadAvailable, GetStreamWriteAvailable
86
@todo review Blocking i/o latency computations in OpenStream(), changing ring
87
buffer to a non-power-of-two structure could reduce blocking i/o latency.
89
@todo implement IsFormatSupported
91
@todo work out how to implement stream stoppage from callback and
92
implement IsStreamActive properly. Stream stoppage could be implemented
93
using a high-priority thread blocked on an Event which is signalled
94
by the callback. Or, we could just call ASIO stop from the callback
97
@todo rigorously check asio return codes and convert to pa error codes
99
@todo Different channels of a multichannel stream can have different sample
100
formats, but we assume that all are the same as the first channel for now.
101
Fixing this will require the block processor to maintain per-channel
102
conversion functions - could get nasty.
104
@todo investigate whether the asio processNow flag needs to be honoured
106
@todo handle asioMessages() callbacks in a useful way, or at least document
107
what cases we don't handle.
109
@todo miscellaneous other FIXMEs
111
@todo provide an asio-specific method for setting the systems specific
112
value (aka main window handle) - check that this matches the value
113
passed to PaAsio_ShowControlPanel, or remove it entirely from
114
PaAsio_ShowControlPanel. - this would allow PaAsio_ShowControlPanel
115
to be called for the currently open stream (at present all streams
124
//#include <values.h>
128
#include <mmsystem.h>
130
#include "portaudio.h"
133
#include "pa_allocation.h"
134
#include "pa_hostapi.h"
135
#include "pa_stream.h"
136
#include "pa_cpuload.h"
137
#include "pa_process.h"
138
#include "pa_debugprint.h"
139
#include "pa_ringbuffer.h"
141
/* This version of pa_asio.cpp is currently only targetted at Win32,
142
It would require a few tweaks to work with pre-OS X Macintosh.
143
To make configuration easier, we define WIN32 here to make sure
144
that the ASIO SDK knows this is Win32.
152
#include "asiodrivers.h"
153
#include "iasiothiscallresolver.h"
165
#include <mmsystem.h>
172
/* external reference to ASIO SDK's asioDrivers.
174
This is a bit messy because we want to explicitly manage
175
allocation/deallocation of this structure, but some layers of the SDK
176
which we currently use (eg the implementation in asio.cpp) still
177
use this global version.
179
For now we keep it in sync with our local instance in the host
180
API representation structure, but later we should be able to remove
181
all dependence on it.
183
extern AsioDrivers* asioDrivers;
186
/* We are trying to be compatible with CARBON but this has not been thoroughly tested. */
187
/* not tested at all since new V19 code was introduced. */
188
#define CARBON_COMPATIBLE (0)
191
/* prototypes for functions declared in this file */
193
extern "C" PaError PaAsio_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex );
194
static void Terminate( struct PaUtilHostApiRepresentation *hostApi );
195
static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
197
const PaStreamParameters *inputParameters,
198
const PaStreamParameters *outputParameters,
200
unsigned long framesPerBuffer,
201
PaStreamFlags streamFlags,
202
PaStreamCallback *streamCallback,
204
static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
205
const PaStreamParameters *inputParameters,
206
const PaStreamParameters *outputParameters,
208
static PaError CloseStream( PaStream* stream );
209
static PaError StartStream( PaStream *stream );
210
static PaError StopStream( PaStream *stream );
211
static PaError AbortStream( PaStream *stream );
212
static PaError IsStreamStopped( PaStream *s );
213
static PaError IsStreamActive( PaStream *stream );
214
static PaTime GetStreamTime( PaStream *stream );
215
static double GetStreamCpuLoad( PaStream* stream );
216
static PaError ReadStream( PaStream* stream, void *buffer, unsigned long frames );
217
static PaError WriteStream( PaStream* stream, const void *buffer, unsigned long frames );
218
static signed long GetStreamReadAvailable( PaStream* stream );
219
static signed long GetStreamWriteAvailable( PaStream* stream );
221
/* Blocking i/o callback function. */
222
static int BlockingIoPaCallback(const void *inputBuffer ,
224
unsigned long framesPerBuffer,
225
const PaStreamCallbackTimeInfo *timeInfo ,
226
PaStreamCallbackFlags statusFlags ,
229
/* our ASIO callback functions */
231
static void bufferSwitch(long index, ASIOBool processNow);
232
static ASIOTime *bufferSwitchTimeInfo(ASIOTime *timeInfo, long index, ASIOBool processNow);
233
static void sampleRateChanged(ASIOSampleRate sRate);
234
static long asioMessages(long selector, long value, void* message, double* opt);
236
static ASIOCallbacks asioCallbacks_ =
237
{ bufferSwitch, sampleRateChanged, asioMessages, bufferSwitchTimeInfo };
240
#define PA_ASIO_SET_LAST_HOST_ERROR( errorCode, errorText ) \
241
PaUtil_SetLastHostErrorInfo( paASIO, errorCode, errorText )
244
static void PaAsio_SetLastSystemError( DWORD errorCode )
248
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
251
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
256
PaUtil_SetLastHostErrorInfo( paASIO, errorCode, (const char*)lpMsgBuf );
257
LocalFree( lpMsgBuf );
260
#define PA_ASIO_SET_LAST_SYSTEM_ERROR( errorCode ) \
261
PaAsio_SetLastSystemError( errorCode )
264
static const char* PaAsio_GetAsioErrorText( ASIOError asioError )
270
case ASE_SUCCESS: result = "Success"; break;
271
case ASE_NotPresent: result = "Hardware input or output is not present or available"; break;
272
case ASE_HWMalfunction: result = "Hardware is malfunctioning"; break;
273
case ASE_InvalidParameter: result = "Input parameter invalid"; break;
274
case ASE_InvalidMode: result = "Hardware is in a bad mode or used in a bad mode"; break;
275
case ASE_SPNotAdvancing: result = "Hardware is not running when sample position is inquired"; break;
276
case ASE_NoClock: result = "Sample clock or rate cannot be determined or is not present"; break;
277
case ASE_NoMemory: result = "Not enough memory for completing the request"; break;
278
default: result = "Unknown ASIO error"; break;
285
#define PA_ASIO_SET_LAST_ASIO_ERROR( asioError ) \
286
PaUtil_SetLastHostErrorInfo( paASIO, asioError, PaAsio_GetAsioErrorText( asioError ) )
291
// Atomic increment and decrement operations
293
/* need to be implemented on Mac */
294
inline long PaAsio_AtomicIncrement(volatile long* v) {return ++(*const_cast<long*>(v));}
295
inline long PaAsio_AtomicDecrement(volatile long* v) {return --(*const_cast<long*>(v));}
297
inline long PaAsio_AtomicIncrement(volatile long* v) {return InterlockedIncrement(const_cast<long*>(v));}
298
inline long PaAsio_AtomicDecrement(volatile long* v) {return InterlockedDecrement(const_cast<long*>(v));}
303
typedef struct PaAsioDriverInfo
305
ASIODriverInfo asioDriverInfo;
306
long inputChannelCount, outputChannelCount;
307
long bufferMinSize, bufferMaxSize, bufferPreferredSize, bufferGranularity;
313
/* PaAsioHostApiRepresentation - host api datastructure specific to this implementation */
317
PaUtilHostApiRepresentation inheritedHostApiRep;
318
PaUtilStreamInterface callbackStreamInterface;
319
PaUtilStreamInterface blockingStreamInterface;
321
PaUtilAllocationGroup *allocations;
323
AsioDrivers *asioDrivers;
324
void *systemSpecific;
326
/* the ASIO C API only allows one ASIO driver to be open at a time,
327
so we keep track of whether we have the driver open here, and
328
use this information to return errors from OpenStream if the
329
driver is already open.
331
openAsioDeviceIndex will be PaNoDevice if there is no device open
332
and a valid pa_asio (not global) device index otherwise.
334
openAsioDriverInfo is populated with the driver info for the
335
currently open device (if any)
337
PaDeviceIndex openAsioDeviceIndex;
338
PaAsioDriverInfo openAsioDriverInfo;
340
PaAsioHostApiRepresentation;
344
Retrieve <driverCount> driver names from ASIO, returned in a char**
345
allocated in <group>.
347
static char **GetAsioDriverNames( PaAsioHostApiRepresentation *asioHostApi, PaUtilAllocationGroup *group, long driverCount )
352
result =(char**)PaUtil_GroupAllocateMemory(
353
group, sizeof(char*) * driverCount );
357
result[0] = (char*)PaUtil_GroupAllocateMemory(
358
group, 32 * driverCount );
362
for( i=0; i<driverCount; ++i )
363
result[i] = result[0] + (32 * i);
365
asioHostApi->asioDrivers->getDriverNames( result, driverCount );
372
static PaSampleFormat AsioSampleTypeToPaNativeSampleFormat(ASIOSampleType type)
379
case ASIOSTFloat32MSB:
380
case ASIOSTFloat32LSB:
381
case ASIOSTFloat64MSB:
382
case ASIOSTFloat64LSB:
387
case ASIOSTInt32MSB16:
388
case ASIOSTInt32LSB16:
389
case ASIOSTInt32MSB18:
390
case ASIOSTInt32MSB20:
391
case ASIOSTInt32MSB24:
392
case ASIOSTInt32LSB18:
393
case ASIOSTInt32LSB20:
394
case ASIOSTInt32LSB24:
402
return paCustomFormat;
406
void AsioSampleTypeLOG(ASIOSampleType type)
409
case ASIOSTInt16MSB: PA_DEBUG(("ASIOSTInt16MSB\n")); break;
410
case ASIOSTInt16LSB: PA_DEBUG(("ASIOSTInt16LSB\n")); break;
411
case ASIOSTFloat32MSB:PA_DEBUG(("ASIOSTFloat32MSB\n"));break;
412
case ASIOSTFloat32LSB:PA_DEBUG(("ASIOSTFloat32LSB\n"));break;
413
case ASIOSTFloat64MSB:PA_DEBUG(("ASIOSTFloat64MSB\n"));break;
414
case ASIOSTFloat64LSB:PA_DEBUG(("ASIOSTFloat64LSB\n"));break;
415
case ASIOSTInt32MSB: PA_DEBUG(("ASIOSTInt32MSB\n")); break;
416
case ASIOSTInt32LSB: PA_DEBUG(("ASIOSTInt32LSB\n")); break;
417
case ASIOSTInt32MSB16:PA_DEBUG(("ASIOSTInt32MSB16\n"));break;
418
case ASIOSTInt32LSB16:PA_DEBUG(("ASIOSTInt32LSB16\n"));break;
419
case ASIOSTInt32MSB18:PA_DEBUG(("ASIOSTInt32MSB18\n"));break;
420
case ASIOSTInt32MSB20:PA_DEBUG(("ASIOSTInt32MSB20\n"));break;
421
case ASIOSTInt32MSB24:PA_DEBUG(("ASIOSTInt32MSB24\n"));break;
422
case ASIOSTInt32LSB18:PA_DEBUG(("ASIOSTInt32LSB18\n"));break;
423
case ASIOSTInt32LSB20:PA_DEBUG(("ASIOSTInt32LSB20\n"));break;
424
case ASIOSTInt32LSB24:PA_DEBUG(("ASIOSTInt32LSB24\n"));break;
425
case ASIOSTInt24MSB: PA_DEBUG(("ASIOSTInt24MSB\n")); break;
426
case ASIOSTInt24LSB: PA_DEBUG(("ASIOSTInt24LSB\n")); break;
427
default: PA_DEBUG(("Custom Format%d\n",type));break;
432
static int BytesPerAsioSample( ASIOSampleType sampleType )
434
switch (sampleType) {
439
case ASIOSTFloat64MSB:
440
case ASIOSTFloat64LSB:
443
case ASIOSTFloat32MSB:
444
case ASIOSTFloat32LSB:
447
case ASIOSTInt32MSB16:
448
case ASIOSTInt32LSB16:
449
case ASIOSTInt32MSB18:
450
case ASIOSTInt32MSB20:
451
case ASIOSTInt32MSB24:
452
case ASIOSTInt32LSB18:
453
case ASIOSTInt32LSB20:
454
case ASIOSTInt32LSB24:
467
static void Swap16( void *buffer, long shift, long count )
469
unsigned short *p = (unsigned short*)buffer;
471
(void) shift; /* unused parameter */
476
*p++ = (unsigned short)((temp<<8) | (temp>>8));
480
static void Swap24( void *buffer, long shift, long count )
482
unsigned char *p = (unsigned char*)buffer;
484
(void) shift; /* unused parameter */
495
#define PA_SWAP32_( x ) ((x>>24) | ((x>>8)&0xFF00) | ((x<<8)&0xFF0000) | (x<<24));
497
static void Swap32( void *buffer, long shift, long count )
499
unsigned long *p = (unsigned long*)buffer;
501
(void) shift; /* unused parameter */
506
*p++ = PA_SWAP32_( temp);
510
static void SwapShiftLeft32( void *buffer, long shift, long count )
512
unsigned long *p = (unsigned long*)buffer;
518
temp = PA_SWAP32_( temp);
519
*p++ = temp << shift;
523
static void ShiftRightSwap32( void *buffer, long shift, long count )
525
unsigned long *p = (unsigned long*)buffer;
531
*p++ = PA_SWAP32_( temp);
535
static void ShiftLeft32( void *buffer, long shift, long count )
537
unsigned long *p = (unsigned long*)buffer;
543
*p++ = temp << shift;
547
static void ShiftRight32( void *buffer, long shift, long count )
549
unsigned long *p = (unsigned long*)buffer;
555
*p++ = temp >> shift;
559
#define PA_SWAP_( x, y ) temp=x; x = y; y = temp;
561
static void Swap64ConvertFloat64ToFloat32( void *buffer, long shift, long count )
563
double *in = (double*)buffer;
564
float *out = (float*)buffer;
567
(void) shift; /* unused parameter */
571
p = (unsigned char*)in;
572
PA_SWAP_( p[0], p[7] );
573
PA_SWAP_( p[1], p[6] );
574
PA_SWAP_( p[2], p[5] );
575
PA_SWAP_( p[3], p[4] );
577
*out++ = (float) (*in++);
581
static void ConvertFloat64ToFloat32( void *buffer, long shift, long count )
583
double *in = (double*)buffer;
584
float *out = (float*)buffer;
585
(void) shift; /* unused parameter */
588
*out++ = (float) (*in++);
591
static void ConvertFloat32ToFloat64Swap64( void *buffer, long shift, long count )
593
float *in = ((float*)buffer) + (count-1);
594
double *out = ((double*)buffer) + (count-1);
597
(void) shift; /* unused parameter */
603
p = (unsigned char*)out;
604
PA_SWAP_( p[0], p[7] );
605
PA_SWAP_( p[1], p[6] );
606
PA_SWAP_( p[2], p[5] );
607
PA_SWAP_( p[3], p[4] );
613
static void ConvertFloat32ToFloat64( void *buffer, long shift, long count )
615
float *in = ((float*)buffer) + (count-1);
616
double *out = ((double*)buffer) + (count-1);
617
(void) shift; /* unused parameter */
624
#define PA_MSB_IS_NATIVE_
625
#undef PA_LSB_IS_NATIVE_
629
#undef PA_MSB_IS_NATIVE_
630
#define PA_LSB_IS_NATIVE_
633
typedef void PaAsioBufferConverter( void *, long, long );
635
static void SelectAsioToPaConverter( ASIOSampleType type, PaAsioBufferConverter **converter, long *shift )
642
/* dest: paInt16, no conversion necessary, possible byte swap*/
643
#ifdef PA_LSB_IS_NATIVE_
648
/* dest: paInt16, no conversion necessary, possible byte swap*/
649
#ifdef PA_MSB_IS_NATIVE_
653
case ASIOSTFloat32MSB:
654
/* dest: paFloat32, no conversion necessary, possible byte swap*/
655
#ifdef PA_LSB_IS_NATIVE_
659
case ASIOSTFloat32LSB:
660
/* dest: paFloat32, no conversion necessary, possible byte swap*/
661
#ifdef PA_MSB_IS_NATIVE_
665
case ASIOSTFloat64MSB:
666
/* dest: paFloat32, in-place conversion to/from float32, possible byte swap*/
667
#ifdef PA_LSB_IS_NATIVE_
668
*converter = Swap64ConvertFloat64ToFloat32;
670
*converter = ConvertFloat64ToFloat32;
673
case ASIOSTFloat64LSB:
674
/* dest: paFloat32, in-place conversion to/from float32, possible byte swap*/
675
#ifdef PA_MSB_IS_NATIVE_
676
*converter = Swap64ConvertFloat64ToFloat32;
678
*converter = ConvertFloat64ToFloat32;
682
/* dest: paInt32, no conversion necessary, possible byte swap */
683
#ifdef PA_LSB_IS_NATIVE_
688
/* dest: paInt32, no conversion necessary, possible byte swap */
689
#ifdef PA_MSB_IS_NATIVE_
693
case ASIOSTInt32MSB16:
694
/* dest: paInt32, 16 bit shift, possible byte swap */
695
#ifdef PA_LSB_IS_NATIVE_
696
*converter = SwapShiftLeft32;
698
*converter = ShiftLeft32;
702
case ASIOSTInt32MSB18:
703
/* dest: paInt32, 14 bit shift, possible byte swap */
704
#ifdef PA_LSB_IS_NATIVE_
705
*converter = SwapShiftLeft32;
707
*converter = ShiftLeft32;
711
case ASIOSTInt32MSB20:
712
/* dest: paInt32, 12 bit shift, possible byte swap */
713
#ifdef PA_LSB_IS_NATIVE_
714
*converter = SwapShiftLeft32;
716
*converter = ShiftLeft32;
720
case ASIOSTInt32MSB24:
721
/* dest: paInt32, 8 bit shift, possible byte swap */
722
#ifdef PA_LSB_IS_NATIVE_
723
*converter = SwapShiftLeft32;
725
*converter = ShiftLeft32;
729
case ASIOSTInt32LSB16:
730
/* dest: paInt32, 16 bit shift, possible byte swap */
731
#ifdef PA_MSB_IS_NATIVE_
732
*converter = SwapShiftLeft32;
734
*converter = ShiftLeft32;
738
case ASIOSTInt32LSB18:
739
/* dest: paInt32, 14 bit shift, possible byte swap */
740
#ifdef PA_MSB_IS_NATIVE_
741
*converter = SwapShiftLeft32;
743
*converter = ShiftLeft32;
747
case ASIOSTInt32LSB20:
748
/* dest: paInt32, 12 bit shift, possible byte swap */
749
#ifdef PA_MSB_IS_NATIVE_
750
*converter = SwapShiftLeft32;
752
*converter = ShiftLeft32;
756
case ASIOSTInt32LSB24:
757
/* dest: paInt32, 8 bit shift, possible byte swap */
758
#ifdef PA_MSB_IS_NATIVE_
759
*converter = SwapShiftLeft32;
761
*converter = ShiftLeft32;
766
/* dest: paInt24, no conversion necessary, possible byte swap */
767
#ifdef PA_LSB_IS_NATIVE_
772
/* dest: paInt24, no conversion necessary, possible byte swap */
773
#ifdef PA_MSB_IS_NATIVE_
781
static void SelectPaToAsioConverter( ASIOSampleType type, PaAsioBufferConverter **converter, long *shift )
788
/* src: paInt16, no conversion necessary, possible byte swap*/
789
#ifdef PA_LSB_IS_NATIVE_
794
/* src: paInt16, no conversion necessary, possible byte swap*/
795
#ifdef PA_MSB_IS_NATIVE_
799
case ASIOSTFloat32MSB:
800
/* src: paFloat32, no conversion necessary, possible byte swap*/
801
#ifdef PA_LSB_IS_NATIVE_
805
case ASIOSTFloat32LSB:
806
/* src: paFloat32, no conversion necessary, possible byte swap*/
807
#ifdef PA_MSB_IS_NATIVE_
811
case ASIOSTFloat64MSB:
812
/* src: paFloat32, in-place conversion to/from float32, possible byte swap*/
813
#ifdef PA_LSB_IS_NATIVE_
814
*converter = ConvertFloat32ToFloat64Swap64;
816
*converter = ConvertFloat32ToFloat64;
819
case ASIOSTFloat64LSB:
820
/* src: paFloat32, in-place conversion to/from float32, possible byte swap*/
821
#ifdef PA_MSB_IS_NATIVE_
822
*converter = ConvertFloat32ToFloat64Swap64;
824
*converter = ConvertFloat32ToFloat64;
828
/* src: paInt32, no conversion necessary, possible byte swap */
829
#ifdef PA_LSB_IS_NATIVE_
834
/* src: paInt32, no conversion necessary, possible byte swap */
835
#ifdef PA_MSB_IS_NATIVE_
839
case ASIOSTInt32MSB16:
840
/* src: paInt32, 16 bit shift, possible byte swap */
841
#ifdef PA_LSB_IS_NATIVE_
842
*converter = ShiftRightSwap32;
844
*converter = ShiftRight32;
848
case ASIOSTInt32MSB18:
849
/* src: paInt32, 14 bit shift, possible byte swap */
850
#ifdef PA_LSB_IS_NATIVE_
851
*converter = ShiftRightSwap32;
853
*converter = ShiftRight32;
857
case ASIOSTInt32MSB20:
858
/* src: paInt32, 12 bit shift, possible byte swap */
859
#ifdef PA_LSB_IS_NATIVE_
860
*converter = ShiftRightSwap32;
862
*converter = ShiftRight32;
866
case ASIOSTInt32MSB24:
867
/* src: paInt32, 8 bit shift, possible byte swap */
868
#ifdef PA_LSB_IS_NATIVE_
869
*converter = ShiftRightSwap32;
871
*converter = ShiftRight32;
875
case ASIOSTInt32LSB16:
876
/* src: paInt32, 16 bit shift, possible byte swap */
877
#ifdef PA_MSB_IS_NATIVE_
878
*converter = ShiftRightSwap32;
880
*converter = ShiftRight32;
884
case ASIOSTInt32LSB18:
885
/* src: paInt32, 14 bit shift, possible byte swap */
886
#ifdef PA_MSB_IS_NATIVE_
887
*converter = ShiftRightSwap32;
889
*converter = ShiftRight32;
893
case ASIOSTInt32LSB20:
894
/* src: paInt32, 12 bit shift, possible byte swap */
895
#ifdef PA_MSB_IS_NATIVE_
896
*converter = ShiftRightSwap32;
898
*converter = ShiftRight32;
902
case ASIOSTInt32LSB24:
903
/* src: paInt32, 8 bit shift, possible byte swap */
904
#ifdef PA_MSB_IS_NATIVE_
905
*converter = ShiftRightSwap32;
907
*converter = ShiftRight32;
912
/* src: paInt24, no conversion necessary, possible byte swap */
913
#ifdef PA_LSB_IS_NATIVE_
918
/* src: paInt24, no conversion necessary, possible byte swap */
919
#ifdef PA_MSB_IS_NATIVE_
927
typedef struct PaAsioDeviceInfo
929
PaDeviceInfo commonDeviceInfo;
932
long preferredBufferSize;
933
long bufferGranularity;
935
ASIOChannelInfo *asioChannelInfos;
940
PaError PaAsio_GetAvailableLatencyValues( PaDeviceIndex device,
941
long *minLatency, long *maxLatency, long *preferredLatency, long *granularity )
944
PaUtilHostApiRepresentation *hostApi;
945
PaDeviceIndex hostApiDevice;
947
result = PaUtil_GetHostApiRepresentation( &hostApi, paASIO );
949
if( result == paNoError )
951
result = PaUtil_DeviceIndexToHostApiDeviceIndex( &hostApiDevice, device, hostApi );
953
if( result == paNoError )
955
PaAsioDeviceInfo *asioDeviceInfo =
956
(PaAsioDeviceInfo*)hostApi->deviceInfos[hostApiDevice];
958
*minLatency = asioDeviceInfo->minBufferSize;
959
*maxLatency = asioDeviceInfo->maxBufferSize;
960
*preferredLatency = asioDeviceInfo->preferredBufferSize;
961
*granularity = asioDeviceInfo->bufferGranularity;
968
/* Unload whatever we loaded in LoadAsioDriver().
969
Also balance the call to CoInitialize(0).
971
static void UnloadAsioDriver( void )
978
load the asio driver named by <driverName> and return statistics about
979
the driver in info. If no error occurred, the driver will remain open
980
and must be closed by the called by calling UnloadAsioDriver() - if an error
981
is returned the driver will already be unloaded.
983
static PaError LoadAsioDriver( PaAsioHostApiRepresentation *asioHostApi, const char *driverName,
984
PaAsioDriverInfo *driverInfo, void *systemSpecific )
986
PaError result = paNoError;
988
int asioIsInitialized = 0;
991
ASIO uses CoCreateInstance() to load a driver. That requires that
992
CoInitialize(0) be called for every thread that loads a driver.
993
It is OK to call CoInitialize(0) multiple times form one thread as long
994
as it is balanced by a call to CoUninitialize(). See UnloadAsioDriver().
996
The V18 version called CoInitialize() starting on 2/19/02.
997
That was removed from PA V19 for unknown reasons.
998
Phil Burk added it back on 6/27/08 so that JSyn would work.
1002
if( !asioHostApi->asioDrivers->loadDriver( const_cast<char*>(driverName) ) )
1004
/* If this returns an error then it might be because CoInitialize(0) was removed.
1005
It should be called right before this.
1007
result = paUnanticipatedHostError;
1008
PA_ASIO_SET_LAST_HOST_ERROR( 0, "Failed to load ASIO driver" );
1012
memset( &driverInfo->asioDriverInfo, 0, sizeof(ASIODriverInfo) );
1013
driverInfo->asioDriverInfo.asioVersion = 2;
1014
driverInfo->asioDriverInfo.sysRef = systemSpecific;
1015
if( (asioError = ASIOInit( &driverInfo->asioDriverInfo )) != ASE_OK )
1017
result = paUnanticipatedHostError;
1018
PA_ASIO_SET_LAST_ASIO_ERROR( asioError );
1023
asioIsInitialized = 1;
1026
if( (asioError = ASIOGetChannels(&driverInfo->inputChannelCount,
1027
&driverInfo->outputChannelCount)) != ASE_OK )
1029
result = paUnanticipatedHostError;
1030
PA_ASIO_SET_LAST_ASIO_ERROR( asioError );
1034
if( (asioError = ASIOGetBufferSize(&driverInfo->bufferMinSize,
1035
&driverInfo->bufferMaxSize, &driverInfo->bufferPreferredSize,
1036
&driverInfo->bufferGranularity)) != ASE_OK )
1038
result = paUnanticipatedHostError;
1039
PA_ASIO_SET_LAST_ASIO_ERROR( asioError );
1043
if( ASIOOutputReady() == ASE_OK )
1044
driverInfo->postOutput = true;
1046
driverInfo->postOutput = false;
1051
if( asioIsInitialized )
1060
#define PA_DEFAULTSAMPLERATESEARCHORDER_COUNT_ 13 /* must be the same number of elements as in the array below */
1061
static ASIOSampleRate defaultSampleRateSearchOrder_[]
1062
= {44100.0, 48000.0, 32000.0, 24000.0, 22050.0, 88200.0, 96000.0,
1063
192000.0, 16000.0, 12000.0, 11025.0, 9600.0, 8000.0 };
1066
/* we look up IsDebuggerPresent at runtime incase it isn't present (on Win95 for example) */
1067
typedef BOOL (WINAPI *IsDebuggerPresentPtr)(VOID);
1068
IsDebuggerPresentPtr IsDebuggerPresent_ = 0;
1069
//FARPROC IsDebuggerPresent_ = 0; // this is the current way to do it apparently according to davidv
1071
PaError PaAsio_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex )
1073
PaError result = paNoError;
1075
PaAsioHostApiRepresentation *asioHostApi;
1076
PaAsioDeviceInfo *deviceInfoArray;
1078
PaAsioDriverInfo paAsioDriverInfo;
1080
asioHostApi = (PaAsioHostApiRepresentation*)PaUtil_AllocateMemory( sizeof(PaAsioHostApiRepresentation) );
1083
result = paInsufficientMemory;
1087
asioHostApi->asioDrivers = 0; /* avoid surprises in our error handler below */
1089
asioHostApi->allocations = PaUtil_CreateAllocationGroup();
1090
if( !asioHostApi->allocations )
1092
result = paInsufficientMemory;
1096
/* Allocate the AsioDrivers() driver list (class from ASIO SDK) */
1099
asioHostApi->asioDrivers = new AsioDrivers(); /* calls CoInitialize(0) */
1101
catch (std::bad_alloc)
1103
asioHostApi->asioDrivers = 0;
1105
/* some implementations of new (ie MSVC, see http://support.microsoft.com/?kbid=167733)
1106
don't throw std::bad_alloc, so we also explicitly test for a null return. */
1107
if( asioHostApi->asioDrivers == 0 )
1109
result = paInsufficientMemory;
1113
asioDrivers = asioHostApi->asioDrivers; /* keep SDK global in sync until we stop depending on it */
1115
asioHostApi->systemSpecific = 0;
1116
asioHostApi->openAsioDeviceIndex = paNoDevice;
1118
*hostApi = &asioHostApi->inheritedHostApiRep;
1119
(*hostApi)->info.structVersion = 1;
1121
(*hostApi)->info.type = paASIO;
1122
(*hostApi)->info.name = "ASIO";
1123
(*hostApi)->info.deviceCount = 0;
1126
/* use desktop window as system specific ptr */
1127
asioHostApi->systemSpecific = GetDesktopWindow();
1130
/* driverCount is the number of installed drivers - not necessarily
1131
the number of installed physical devices. */
1133
driverCount = asioHostApi->asioDrivers->getNumFragments();
1135
driverCount = asioHostApi->asioDrivers->asioGetNumDev();
1138
if( driverCount > 0 )
1140
names = GetAsioDriverNames( asioHostApi, asioHostApi->allocations, driverCount );
1143
result = paInsufficientMemory;
1148
/* allocate enough space for all drivers, even if some aren't installed */
1150
(*hostApi)->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory(
1151
asioHostApi->allocations, sizeof(PaDeviceInfo*) * driverCount );
1152
if( !(*hostApi)->deviceInfos )
1154
result = paInsufficientMemory;
1158
/* allocate all device info structs in a contiguous block */
1159
deviceInfoArray = (PaAsioDeviceInfo*)PaUtil_GroupAllocateMemory(
1160
asioHostApi->allocations, sizeof(PaAsioDeviceInfo) * driverCount );
1161
if( !deviceInfoArray )
1163
result = paInsufficientMemory;
1167
IsDebuggerPresent_ = GetProcAddress( LoadLibrary( "Kernel32.dll" ), "IsDebuggerPresent" );
1169
for( i=0; i < driverCount; ++i )
1172
PA_DEBUG(("ASIO names[%d]:%s\n",i,names[i]));
1174
// Since portaudio opens ALL ASIO drivers, and no one else does that,
1175
// we face fact that some drivers were not meant for it, drivers which act
1176
// like shells on top of REAL drivers, for instance.
1177
// so we get duplicate handles, locks and other problems.
1178
// so lets NOT try to load any such wrappers.
1179
// The ones i [davidv] know of so far are:
1181
if ( strcmp (names[i],"ASIO DirectX Full Duplex Driver") == 0
1182
|| strcmp (names[i],"ASIO Multimedia Driver") == 0
1183
|| strncmp(names[i],"Premiere",8) == 0 //"Premiere Elements Windows Sound 1.0"
1184
|| strncmp(names[i],"Adobe",5) == 0 //"Adobe Default Windows Sound 1.5"
1187
PA_DEBUG(("BLACKLISTED!!!\n"));
1192
if( IsDebuggerPresent_ && IsDebuggerPresent_() )
1194
/* ASIO Digidesign Driver uses PACE copy protection which quits out
1195
if a debugger is running. So we don't load it if a debugger is running. */
1196
if( strcmp(names[i], "ASIO Digidesign Driver") == 0 )
1198
PA_DEBUG(("BLACKLISTED!!! ASIO Digidesign Driver would quit the debugger\n"));
1204
/* Attempt to load the asio driver... */
1205
if( LoadAsioDriver( asioHostApi, names[i], &paAsioDriverInfo, asioHostApi->systemSpecific ) == paNoError )
1207
PaAsioDeviceInfo *asioDeviceInfo = &deviceInfoArray[ (*hostApi)->info.deviceCount ];
1208
PaDeviceInfo *deviceInfo = &asioDeviceInfo->commonDeviceInfo;
1210
deviceInfo->structVersion = 2;
1211
deviceInfo->hostApi = hostApiIndex;
1213
deviceInfo->name = names[i];
1214
PA_DEBUG(("PaAsio_Initialize: drv:%d name = %s\n", i,deviceInfo->name));
1215
PA_DEBUG(("PaAsio_Initialize: drv:%d inputChannels = %d\n", i, paAsioDriverInfo.inputChannelCount));
1216
PA_DEBUG(("PaAsio_Initialize: drv:%d outputChannels = %d\n", i, paAsioDriverInfo.outputChannelCount));
1217
PA_DEBUG(("PaAsio_Initialize: drv:%d bufferMinSize = %d\n", i, paAsioDriverInfo.bufferMinSize));
1218
PA_DEBUG(("PaAsio_Initialize: drv:%d bufferMaxSize = %d\n", i, paAsioDriverInfo.bufferMaxSize));
1219
PA_DEBUG(("PaAsio_Initialize: drv:%d bufferPreferredSize = %d\n", i, paAsioDriverInfo.bufferPreferredSize));
1220
PA_DEBUG(("PaAsio_Initialize: drv:%d bufferGranularity = %d\n", i, paAsioDriverInfo.bufferGranularity));
1222
deviceInfo->maxInputChannels = paAsioDriverInfo.inputChannelCount;
1223
deviceInfo->maxOutputChannels = paAsioDriverInfo.outputChannelCount;
1225
deviceInfo->defaultSampleRate = 0.;
1226
bool foundDefaultSampleRate = false;
1227
for( int j=0; j < PA_DEFAULTSAMPLERATESEARCHORDER_COUNT_; ++j )
1229
ASIOError asioError = ASIOCanSampleRate( defaultSampleRateSearchOrder_[j] );
1230
if( asioError != ASE_NoClock && asioError != ASE_NotPresent )
1232
deviceInfo->defaultSampleRate = defaultSampleRateSearchOrder_[j];
1233
foundDefaultSampleRate = true;
1238
PA_DEBUG(("PaAsio_Initialize: drv:%d defaultSampleRate = %f\n", i, deviceInfo->defaultSampleRate));
1240
if( foundDefaultSampleRate ){
1242
/* calculate default latency values from bufferPreferredSize
1243
for default low latency, and bufferPreferredSize * 3
1244
for default high latency.
1245
use the default sample rate to convert from samples to
1246
seconds. Without knowing what sample rate the user will
1247
use this is the best we can do.
1250
double defaultLowLatency =
1251
paAsioDriverInfo.bufferPreferredSize / deviceInfo->defaultSampleRate;
1253
deviceInfo->defaultLowInputLatency = defaultLowLatency;
1254
deviceInfo->defaultLowOutputLatency = defaultLowLatency;
1256
long defaultHighLatencyBufferSize =
1257
paAsioDriverInfo.bufferPreferredSize * 3;
1259
if( defaultHighLatencyBufferSize > paAsioDriverInfo.bufferMaxSize )
1260
defaultHighLatencyBufferSize = paAsioDriverInfo.bufferMaxSize;
1262
double defaultHighLatency =
1263
defaultHighLatencyBufferSize / deviceInfo->defaultSampleRate;
1265
if( defaultHighLatency < defaultLowLatency )
1266
defaultHighLatency = defaultLowLatency; /* just incase the driver returns something strange */
1268
deviceInfo->defaultHighInputLatency = defaultHighLatency;
1269
deviceInfo->defaultHighOutputLatency = defaultHighLatency;
1273
deviceInfo->defaultLowInputLatency = 0.;
1274
deviceInfo->defaultLowOutputLatency = 0.;
1275
deviceInfo->defaultHighInputLatency = 0.;
1276
deviceInfo->defaultHighOutputLatency = 0.;
1279
PA_DEBUG(("PaAsio_Initialize: drv:%d defaultLowInputLatency = %f\n", i, deviceInfo->defaultLowInputLatency));
1280
PA_DEBUG(("PaAsio_Initialize: drv:%d defaultLowOutputLatency = %f\n", i, deviceInfo->defaultLowOutputLatency));
1281
PA_DEBUG(("PaAsio_Initialize: drv:%d defaultHighInputLatency = %f\n", i, deviceInfo->defaultHighInputLatency));
1282
PA_DEBUG(("PaAsio_Initialize: drv:%d defaultHighOutputLatency = %f\n", i, deviceInfo->defaultHighOutputLatency));
1284
asioDeviceInfo->minBufferSize = paAsioDriverInfo.bufferMinSize;
1285
asioDeviceInfo->maxBufferSize = paAsioDriverInfo.bufferMaxSize;
1286
asioDeviceInfo->preferredBufferSize = paAsioDriverInfo.bufferPreferredSize;
1287
asioDeviceInfo->bufferGranularity = paAsioDriverInfo.bufferGranularity;
1290
asioDeviceInfo->asioChannelInfos = (ASIOChannelInfo*)PaUtil_GroupAllocateMemory(
1291
asioHostApi->allocations,
1292
sizeof(ASIOChannelInfo) * (deviceInfo->maxInputChannels
1293
+ deviceInfo->maxOutputChannels) );
1294
if( !asioDeviceInfo->asioChannelInfos )
1296
result = paInsufficientMemory;
1302
for( a=0; a < deviceInfo->maxInputChannels; ++a ){
1303
asioDeviceInfo->asioChannelInfos[a].channel = a;
1304
asioDeviceInfo->asioChannelInfos[a].isInput = ASIOTrue;
1305
ASIOError asioError = ASIOGetChannelInfo( &asioDeviceInfo->asioChannelInfos[a] );
1306
if( asioError != ASE_OK )
1308
result = paUnanticipatedHostError;
1309
PA_ASIO_SET_LAST_ASIO_ERROR( asioError );
1314
for( a=0; a < deviceInfo->maxOutputChannels; ++a ){
1315
int b = deviceInfo->maxInputChannels + a;
1316
asioDeviceInfo->asioChannelInfos[b].channel = a;
1317
asioDeviceInfo->asioChannelInfos[b].isInput = ASIOFalse;
1318
ASIOError asioError = ASIOGetChannelInfo( &asioDeviceInfo->asioChannelInfos[b] );
1319
if( asioError != ASE_OK )
1321
result = paUnanticipatedHostError;
1322
PA_ASIO_SET_LAST_ASIO_ERROR( asioError );
1328
/* unload the driver */
1331
(*hostApi)->deviceInfos[ (*hostApi)->info.deviceCount ] = deviceInfo;
1332
++(*hostApi)->info.deviceCount;
1337
if( (*hostApi)->info.deviceCount > 0 )
1339
(*hostApi)->info.defaultInputDevice = 0;
1340
(*hostApi)->info.defaultOutputDevice = 0;
1344
(*hostApi)->info.defaultInputDevice = paNoDevice;
1345
(*hostApi)->info.defaultOutputDevice = paNoDevice;
1349
(*hostApi)->Terminate = Terminate;
1350
(*hostApi)->OpenStream = OpenStream;
1351
(*hostApi)->IsFormatSupported = IsFormatSupported;
1353
PaUtil_InitializeStreamInterface( &asioHostApi->callbackStreamInterface, CloseStream, StartStream,
1354
StopStream, AbortStream, IsStreamStopped, IsStreamActive,
1355
GetStreamTime, GetStreamCpuLoad,
1356
PaUtil_DummyRead, PaUtil_DummyWrite,
1357
PaUtil_DummyGetReadAvailable, PaUtil_DummyGetWriteAvailable );
1359
PaUtil_InitializeStreamInterface( &asioHostApi->blockingStreamInterface, CloseStream, StartStream,
1360
StopStream, AbortStream, IsStreamStopped, IsStreamActive,
1361
GetStreamTime, PaUtil_DummyGetCpuLoad,
1362
ReadStream, WriteStream, GetStreamReadAvailable, GetStreamWriteAvailable );
1372
if( asioHostApi->allocations )
1374
PaUtil_FreeAllAllocations( asioHostApi->allocations );
1375
PaUtil_DestroyAllocationGroup( asioHostApi->allocations );
1378
delete asioHostApi->asioDrivers;
1379
asioDrivers = 0; /* keep SDK global in sync until we stop depending on it */
1381
PaUtil_FreeMemory( asioHostApi );
1387
static void Terminate( struct PaUtilHostApiRepresentation *hostApi )
1389
PaAsioHostApiRepresentation *asioHostApi = (PaAsioHostApiRepresentation*)hostApi;
1393
- clean up any resources not handled by the allocation group (need to review if there are any)
1396
if( asioHostApi->allocations )
1398
PaUtil_FreeAllAllocations( asioHostApi->allocations );
1399
PaUtil_DestroyAllocationGroup( asioHostApi->allocations );
1402
delete asioHostApi->asioDrivers; /* calls CoUninitialize() */
1403
asioDrivers = 0; /* keep SDK global in sync until we stop depending on it */
1405
PaUtil_FreeMemory( asioHostApi );
1409
static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
1410
const PaStreamParameters *inputParameters,
1411
const PaStreamParameters *outputParameters,
1414
PaError result = paNoError;
1415
PaAsioHostApiRepresentation *asioHostApi = (PaAsioHostApiRepresentation*)hostApi;
1416
PaAsioDriverInfo *driverInfo = &asioHostApi->openAsioDriverInfo;
1417
int inputChannelCount, outputChannelCount;
1418
PaSampleFormat inputSampleFormat, outputSampleFormat;
1419
PaDeviceIndex asioDeviceIndex;
1420
ASIOError asioError;
1422
if( inputParameters && outputParameters )
1424
/* full duplex ASIO stream must use the same device for input and output */
1426
if( inputParameters->device != outputParameters->device )
1427
return paBadIODeviceCombination;
1430
if( inputParameters )
1432
inputChannelCount = inputParameters->channelCount;
1433
inputSampleFormat = inputParameters->sampleFormat;
1435
/* all standard sample formats are supported by the buffer adapter,
1436
this implementation doesn't support any custom sample formats */
1437
if( inputSampleFormat & paCustomFormat )
1438
return paSampleFormatNotSupported;
1440
/* unless alternate device specification is supported, reject the use of
1441
paUseHostApiSpecificDeviceSpecification */
1443
if( inputParameters->device == paUseHostApiSpecificDeviceSpecification )
1444
return paInvalidDevice;
1446
asioDeviceIndex = inputParameters->device;
1448
/* validate inputStreamInfo */
1449
/** @todo do more validation here */
1450
// if( inputParameters->hostApiSpecificStreamInfo )
1451
// return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
1455
inputChannelCount = 0;
1458
if( outputParameters )
1460
outputChannelCount = outputParameters->channelCount;
1461
outputSampleFormat = outputParameters->sampleFormat;
1463
/* all standard sample formats are supported by the buffer adapter,
1464
this implementation doesn't support any custom sample formats */
1465
if( outputSampleFormat & paCustomFormat )
1466
return paSampleFormatNotSupported;
1468
/* unless alternate device specification is supported, reject the use of
1469
paUseHostApiSpecificDeviceSpecification */
1471
if( outputParameters->device == paUseHostApiSpecificDeviceSpecification )
1472
return paInvalidDevice;
1474
asioDeviceIndex = outputParameters->device;
1476
/* validate outputStreamInfo */
1477
/** @todo do more validation here */
1478
// if( outputParameters->hostApiSpecificStreamInfo )
1479
// return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
1483
outputChannelCount = 0;
1488
/* if an ASIO device is open we can only get format information for the currently open device */
1490
if( asioHostApi->openAsioDeviceIndex != paNoDevice
1491
&& asioHostApi->openAsioDeviceIndex != asioDeviceIndex )
1493
return paDeviceUnavailable;
1497
/* NOTE: we load the driver and use its current settings
1498
rather than the ones in our device info structure which may be stale */
1500
/* open the device if it's not already open */
1501
if( asioHostApi->openAsioDeviceIndex == paNoDevice )
1503
result = LoadAsioDriver( asioHostApi, asioHostApi->inheritedHostApiRep.deviceInfos[ asioDeviceIndex ]->name,
1504
driverInfo, asioHostApi->systemSpecific );
1505
if( result != paNoError )
1509
/* check that input device can support inputChannelCount */
1510
if( inputChannelCount > 0 )
1512
if( inputChannelCount > driverInfo->inputChannelCount )
1514
result = paInvalidChannelCount;
1519
/* check that output device can support outputChannelCount */
1520
if( outputChannelCount )
1522
if( outputChannelCount > driverInfo->outputChannelCount )
1524
result = paInvalidChannelCount;
1529
/* query for sample rate support */
1530
asioError = ASIOCanSampleRate( sampleRate );
1531
if( asioError == ASE_NoClock || asioError == ASE_NotPresent )
1533
result = paInvalidSampleRate;
1538
/* close the device if it wasn't already open */
1539
if( asioHostApi->openAsioDeviceIndex == paNoDevice )
1541
UnloadAsioDriver(); /* not sure if we should check for errors here */
1544
if( result == paNoError )
1545
return paFormatIsSupported;
1552
/** A data structure specifically for storing blocking i/o related data. */
1553
typedef struct PaAsioStreamBlockingState
1555
int stopFlag; /**< Flag indicating that block processing is to be stopped. */
1557
unsigned long writeBuffersRequested; /**< The number of available output buffers, requested by the #WriteStream() function. */
1558
unsigned long readFramesRequested; /**< The number of available input frames, requested by the #ReadStream() function. */
1560
int writeBuffersRequestedFlag; /**< Flag to indicate that #WriteStream() has requested more output buffers to be available. */
1561
int readFramesRequestedFlag; /**< Flag to indicate that #ReadStream() requires more input frames to be available. */
1563
HANDLE writeBuffersReadyEvent; /**< Event to signal that requested output buffers are available. */
1564
HANDLE readFramesReadyEvent; /**< Event to signal that requested input frames are available. */
1566
void *writeRingBufferData; /**< The actual ring buffer memory, used by the output ring buffer. */
1567
void *readRingBufferData; /**< The actual ring buffer memory, used by the input ring buffer. */
1569
PaUtilRingBuffer writeRingBuffer; /**< Frame-aligned blocking i/o ring buffer to store output data (interleaved user format). */
1570
PaUtilRingBuffer readRingBuffer; /**< Frame-aligned blocking i/o ring buffer to store input data (interleaved user format). */
1572
long writeRingBufferInitialFrames; /**< The initial number of silent frames within the output ring buffer. */
1574
const void **writeStreamBuffer; /**< Temp buffer, used by #WriteStream() for handling non-interleaved data. */
1575
void **readStreamBuffer; /**< Temp buffer, used by #ReadStream() for handling non-interleaved data. */
1577
PaUtilBufferProcessor bufferProcessor; /**< Buffer processor, used to handle the blocking i/o ring buffers. */
1579
int outputUnderflowFlag; /**< Flag to signal an output underflow from within the callback function. */
1580
int inputOverflowFlag; /**< Flag to signal an input overflow from within the callback function. */
1582
PaAsioStreamBlockingState;
1586
/* PaAsioStream - a stream data structure specifically for this implementation */
1588
typedef struct PaAsioStream
1590
PaUtilStreamRepresentation streamRepresentation;
1591
PaUtilCpuLoadMeasurer cpuLoadMeasurer;
1592
PaUtilBufferProcessor bufferProcessor;
1594
PaAsioHostApiRepresentation *asioHostApi;
1595
unsigned long framesPerHostCallback;
1597
/* ASIO driver info - these may not be needed for the life of the stream,
1598
but store them here until we work out how format conversion is going
1601
ASIOBufferInfo *asioBufferInfos;
1602
ASIOChannelInfo *asioChannelInfos;
1603
long inputLatency, outputLatency; // actual latencies returned by asio
1605
long inputChannelCount, outputChannelCount;
1608
void **bufferPtrs; /* this is carved up for inputBufferPtrs and outputBufferPtrs */
1609
void **inputBufferPtrs[2];
1610
void **outputBufferPtrs[2];
1612
PaAsioBufferConverter *inputBufferConverter;
1614
PaAsioBufferConverter *outputBufferConverter;
1617
volatile bool stopProcessing;
1618
int stopPlayoutCount;
1619
HANDLE completedBuffersPlayedEvent;
1621
bool streamFinishedCallbackCalled;
1623
volatile int isActive;
1624
volatile bool zeroOutput; /* all future calls to the callback will output silence */
1626
volatile long reenterCount;
1627
volatile long reenterError;
1629
PaStreamCallbackFlags callbackFlags;
1631
PaAsioStreamBlockingState *blockingState; /**< Blocking i/o data struct, or NULL when using callback interface. */
1635
static PaAsioStream *theAsioStream = 0; /* due to ASIO sdk limitations there can be only one stream */
1638
static void ZeroOutputBuffers( PaAsioStream *stream, long index )
1642
for( i=0; i < stream->outputChannelCount; ++i )
1644
void *buffer = stream->asioBufferInfos[ i + stream->inputChannelCount ].buffers[index];
1646
int bytesPerSample = BytesPerAsioSample( stream->asioChannelInfos[ i + stream->inputChannelCount ].type );
1648
memset( buffer, 0, stream->framesPerHostCallback * bytesPerSample );
1653
static unsigned long SelectHostBufferSize( unsigned long suggestedLatencyFrames,
1654
PaAsioDriverInfo *driverInfo )
1656
unsigned long result;
1658
if( suggestedLatencyFrames == 0 )
1660
result = driverInfo->bufferPreferredSize;
1663
if( suggestedLatencyFrames <= (unsigned long)driverInfo->bufferMinSize )
1665
result = driverInfo->bufferMinSize;
1667
else if( suggestedLatencyFrames >= (unsigned long)driverInfo->bufferMaxSize )
1669
result = driverInfo->bufferMaxSize;
1673
if( driverInfo->bufferGranularity == -1 )
1678
while( result < suggestedLatencyFrames )
1681
if( result < (unsigned long)driverInfo->bufferMinSize )
1682
result = driverInfo->bufferMinSize;
1684
if( result > (unsigned long)driverInfo->bufferMaxSize )
1685
result = driverInfo->bufferMaxSize;
1687
else if( driverInfo->bufferGranularity == 0 )
1689
/* the documentation states that bufferGranularity should be
1690
zero when bufferMinSize, bufferMaxSize and
1691
bufferPreferredSize are the same. We assume that is the case.
1694
result = driverInfo->bufferPreferredSize;
1698
/* modulo granularity */
1700
unsigned long remainder =
1701
suggestedLatencyFrames % driverInfo->bufferGranularity;
1703
if( remainder == 0 )
1705
result = suggestedLatencyFrames;
1709
result = suggestedLatencyFrames
1710
+ (driverInfo->bufferGranularity - remainder);
1712
if( result > (unsigned long)driverInfo->bufferMaxSize )
1713
result = driverInfo->bufferMaxSize;
1723
/* returns channelSelectors if present */
1725
static PaError ValidateAsioSpecificStreamInfo(
1726
const PaStreamParameters *streamParameters,
1727
const PaAsioStreamInfo *streamInfo,
1728
int deviceChannelCount,
1729
int **channelSelectors )
1733
if( streamInfo->size != sizeof( PaAsioStreamInfo )
1734
|| streamInfo->version != 1 )
1736
return paIncompatibleHostApiSpecificStreamInfo;
1739
if( streamInfo->flags & paAsioUseChannelSelectors )
1740
*channelSelectors = streamInfo->channelSelectors;
1742
if( !(*channelSelectors) )
1743
return paIncompatibleHostApiSpecificStreamInfo;
1745
for( int i=0; i < streamParameters->channelCount; ++i ){
1746
if( (*channelSelectors)[i] < 0
1747
|| (*channelSelectors)[i] >= deviceChannelCount ){
1748
return paInvalidChannelCount;
1757
static bool IsUsingExternalClockSource()
1759
bool result = false;
1760
ASIOError asioError;
1761
ASIOClockSource clocks[32];
1764
/* davidv: listing ASIO Clock sources. there is an ongoing investigation by
1765
me about whether or not to call ASIOSetSampleRate if an external Clock is
1766
used. A few drivers expected different things here */
1768
asioError = ASIOGetClockSources(clocks, &numSources);
1769
if( asioError != ASE_OK ){
1770
PA_DEBUG(("ERROR: ASIOGetClockSources: %s\n", PaAsio_GetAsioErrorText(asioError) ));
1772
PA_DEBUG(("INFO ASIOGetClockSources listing %d clocks\n", numSources ));
1773
for (int i=0;i<numSources;++i){
1774
PA_DEBUG(("ASIOClockSource%d %s current:%d\n", i, clocks[i].name, clocks[i].isCurrentSource ));
1776
if (clocks[i].isCurrentSource)
1785
static PaError ValidateAndSetSampleRate( double sampleRate )
1787
PaError result = paNoError;
1788
ASIOError asioError;
1790
// check that the device supports the requested sample rate
1792
asioError = ASIOCanSampleRate( sampleRate );
1793
PA_DEBUG(("ASIOCanSampleRate(%f):%d\n", sampleRate, asioError ));
1795
if( asioError != ASE_OK )
1797
result = paInvalidSampleRate;
1798
PA_DEBUG(("ERROR: ASIOCanSampleRate: %s\n", PaAsio_GetAsioErrorText(asioError) ));
1802
// retrieve the current sample rate, we only change to the requested
1803
// sample rate if the device is not already in that rate.
1805
ASIOSampleRate oldRate;
1806
asioError = ASIOGetSampleRate(&oldRate);
1807
if( asioError != ASE_OK )
1809
result = paInvalidSampleRate;
1810
PA_DEBUG(("ERROR: ASIOGetSampleRate: %s\n", PaAsio_GetAsioErrorText(asioError) ));
1813
PA_DEBUG(("ASIOGetSampleRate:%f\n",oldRate));
1815
if (oldRate != sampleRate){
1816
/* Set sample rate */
1818
PA_DEBUG(("before ASIOSetSampleRate(%f)\n",sampleRate));
1821
If you have problems with some drivers when externally clocked,
1822
try switching on the following line and commenting out the one after it.
1823
See IsUsingExternalClockSource() for more info.
1825
//if( IsUsingExternalClockSource() ){
1827
asioError = ASIOSetSampleRate( 0 );
1829
asioError = ASIOSetSampleRate( sampleRate );
1831
if( asioError != ASE_OK )
1833
result = paInvalidSampleRate;
1834
PA_DEBUG(("ERROR: ASIOSetSampleRate: %s\n", PaAsio_GetAsioErrorText(asioError) ));
1837
PA_DEBUG(("after ASIOSetSampleRate(%f)\n",sampleRate));
1841
PA_DEBUG(("No Need to change SR\n"));
1849
/* see pa_hostapi.h for a list of validity guarantees made about OpenStream parameters */
1851
static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
1853
const PaStreamParameters *inputParameters,
1854
const PaStreamParameters *outputParameters,
1856
unsigned long framesPerBuffer,
1857
PaStreamFlags streamFlags,
1858
PaStreamCallback *streamCallback,
1861
PaError result = paNoError;
1862
PaAsioHostApiRepresentation *asioHostApi = (PaAsioHostApiRepresentation*)hostApi;
1863
PaAsioStream *stream = 0;
1864
PaAsioStreamInfo *inputStreamInfo, *outputStreamInfo;
1865
unsigned long framesPerHostBuffer;
1866
int inputChannelCount, outputChannelCount;
1867
PaSampleFormat inputSampleFormat, outputSampleFormat;
1868
PaSampleFormat hostInputSampleFormat, hostOutputSampleFormat;
1869
unsigned long suggestedInputLatencyFrames;
1870
unsigned long suggestedOutputLatencyFrames;
1871
PaDeviceIndex asioDeviceIndex;
1872
ASIOError asioError;
1873
int asioIsInitialized = 0;
1874
int asioBuffersCreated = 0;
1875
int completedBuffersPlayedEventInited = 0;
1877
PaAsioDriverInfo *driverInfo;
1878
int *inputChannelSelectors = 0;
1879
int *outputChannelSelectors = 0;
1881
/* Are we using blocking i/o interface? */
1882
int usingBlockingIo = ( !streamCallback ) ? TRUE : FALSE;
1883
/* Blocking i/o stuff */
1884
long lBlockingBufferSize = 0; /* Desired ring buffer size in samples. */
1885
long lBlockingBufferSizePow2 = 0; /* Power-of-2 rounded ring buffer size. */
1886
long lBytesPerFrame = 0; /* Number of bytes per input/output frame. */
1887
int blockingWriteBuffersReadyEventInitialized = 0; /* Event init flag. */
1888
int blockingReadFramesReadyEventInitialized = 0; /* Event init flag. */
1890
int callbackBufferProcessorInited = FALSE;
1891
int blockingBufferProcessorInited = FALSE;
1893
/* unless we move to using lower level ASIO calls, we can only have
1894
one device open at a time */
1895
if( asioHostApi->openAsioDeviceIndex != paNoDevice )
1897
PA_DEBUG(("OpenStream paDeviceUnavailable\n"));
1898
return paDeviceUnavailable;
1901
assert( theAsioStream == 0 );
1903
if( inputParameters && outputParameters )
1905
/* full duplex ASIO stream must use the same device for input and output */
1907
if( inputParameters->device != outputParameters->device )
1909
PA_DEBUG(("OpenStream paBadIODeviceCombination\n"));
1910
return paBadIODeviceCombination;
1914
if( inputParameters )
1916
inputChannelCount = inputParameters->channelCount;
1917
inputSampleFormat = inputParameters->sampleFormat;
1918
suggestedInputLatencyFrames = (unsigned long)((inputParameters->suggestedLatency * sampleRate)+0.5f);
1920
/* unless alternate device specification is supported, reject the use of
1921
paUseHostApiSpecificDeviceSpecification */
1922
if( inputParameters->device == paUseHostApiSpecificDeviceSpecification )
1923
return paInvalidDevice;
1925
asioDeviceIndex = inputParameters->device;
1927
PaAsioDeviceInfo *asioDeviceInfo = (PaAsioDeviceInfo*)hostApi->deviceInfos[asioDeviceIndex];
1929
/* validate hostApiSpecificStreamInfo */
1930
inputStreamInfo = (PaAsioStreamInfo*)inputParameters->hostApiSpecificStreamInfo;
1931
result = ValidateAsioSpecificStreamInfo( inputParameters, inputStreamInfo,
1932
asioDeviceInfo->commonDeviceInfo.maxInputChannels,
1933
&inputChannelSelectors
1935
if( result != paNoError ) return result;
1939
inputChannelCount = 0;
1940
inputSampleFormat = 0;
1941
suggestedInputLatencyFrames = 0;
1944
if( outputParameters )
1946
outputChannelCount = outputParameters->channelCount;
1947
outputSampleFormat = outputParameters->sampleFormat;
1948
suggestedOutputLatencyFrames = (unsigned long)((outputParameters->suggestedLatency * sampleRate)+0.5f);
1950
/* unless alternate device specification is supported, reject the use of
1951
paUseHostApiSpecificDeviceSpecification */
1952
if( outputParameters->device == paUseHostApiSpecificDeviceSpecification )
1953
return paInvalidDevice;
1955
asioDeviceIndex = outputParameters->device;
1957
PaAsioDeviceInfo *asioDeviceInfo = (PaAsioDeviceInfo*)hostApi->deviceInfos[asioDeviceIndex];
1959
/* validate hostApiSpecificStreamInfo */
1960
outputStreamInfo = (PaAsioStreamInfo*)outputParameters->hostApiSpecificStreamInfo;
1961
result = ValidateAsioSpecificStreamInfo( outputParameters, outputStreamInfo,
1962
asioDeviceInfo->commonDeviceInfo.maxOutputChannels,
1963
&outputChannelSelectors
1965
if( result != paNoError ) return result;
1969
outputChannelCount = 0;
1970
outputSampleFormat = 0;
1971
suggestedOutputLatencyFrames = 0;
1974
driverInfo = &asioHostApi->openAsioDriverInfo;
1976
/* NOTE: we load the driver and use its current settings
1977
rather than the ones in our device info structure which may be stale */
1979
result = LoadAsioDriver( asioHostApi, asioHostApi->inheritedHostApiRep.deviceInfos[ asioDeviceIndex ]->name,
1980
driverInfo, asioHostApi->systemSpecific );
1981
if( result == paNoError )
1982
asioIsInitialized = 1;
1984
PA_DEBUG(("OpenStream ERROR1 - LoadAsioDriver returned %d\n", result));
1988
/* check that input device can support inputChannelCount */
1989
if( inputChannelCount > 0 )
1991
if( inputChannelCount > driverInfo->inputChannelCount )
1993
result = paInvalidChannelCount;
1994
PA_DEBUG(("OpenStream ERROR2\n"));
1999
/* check that output device can support outputChannelCount */
2000
if( outputChannelCount )
2002
if( outputChannelCount > driverInfo->outputChannelCount )
2004
result = paInvalidChannelCount;
2005
PA_DEBUG(("OpenStream ERROR3\n"));
2010
result = ValidateAndSetSampleRate( sampleRate );
2011
if( result != paNoError )
2016
- if a full duplex stream is requested, check that the combination
2017
of input and output parameters is supported
2020
/* validate platform specific flags */
2021
if( (streamFlags & paPlatformSpecificFlags) != 0 ){
2022
PA_DEBUG(("OpenStream invalid flags!!\n"));
2023
return paInvalidFlag; /* unexpected platform specific flag */
2027
stream = (PaAsioStream*)PaUtil_AllocateMemory( sizeof(PaAsioStream) );
2030
result = paInsufficientMemory;
2031
PA_DEBUG(("OpenStream ERROR5\n"));
2034
stream->blockingState = NULL; /* Blocking i/o not initialized, yet. */
2037
stream->completedBuffersPlayedEvent = CreateEvent( NULL, TRUE, FALSE, NULL );
2038
if( stream->completedBuffersPlayedEvent == NULL )
2040
result = paUnanticipatedHostError;
2041
PA_ASIO_SET_LAST_SYSTEM_ERROR( GetLastError() );
2042
PA_DEBUG(("OpenStream ERROR6\n"));
2045
completedBuffersPlayedEventInited = 1;
2048
stream->asioBufferInfos = 0; /* for deallocation in error */
2049
stream->asioChannelInfos = 0; /* for deallocation in error */
2050
stream->bufferPtrs = 0; /* for deallocation in error */
2052
/* Using blocking i/o interface... */
2053
if( usingBlockingIo )
2055
/* Blocking i/o is implemented by running callback mode, using a special blocking i/o callback. */
2056
streamCallback = BlockingIoPaCallback; /* Setup PA to use the ASIO blocking i/o callback. */
2057
userData = &theAsioStream; /* The callback user data will be the PA ASIO stream. */
2058
PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation,
2059
&asioHostApi->blockingStreamInterface, streamCallback, userData );
2061
else /* Using callback interface... */
2063
PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation,
2064
&asioHostApi->callbackStreamInterface, streamCallback, userData );
2068
PaUtil_InitializeCpuLoadMeasurer( &stream->cpuLoadMeasurer, sampleRate );
2071
stream->asioBufferInfos = (ASIOBufferInfo*)PaUtil_AllocateMemory(
2072
sizeof(ASIOBufferInfo) * (inputChannelCount + outputChannelCount) );
2073
if( !stream->asioBufferInfos )
2075
result = paInsufficientMemory;
2076
PA_DEBUG(("OpenStream ERROR7\n"));
2081
for( i=0; i < inputChannelCount; ++i )
2083
ASIOBufferInfo *info = &stream->asioBufferInfos[i];
2085
info->isInput = ASIOTrue;
2087
if( inputChannelSelectors ){
2088
// inputChannelSelectors values have already been validated in
2089
// ValidateAsioSpecificStreamInfo() above
2090
info->channelNum = inputChannelSelectors[i];
2092
info->channelNum = i;
2095
info->buffers[0] = info->buffers[1] = 0;
2098
for( i=0; i < outputChannelCount; ++i ){
2099
ASIOBufferInfo *info = &stream->asioBufferInfos[inputChannelCount+i];
2101
info->isInput = ASIOFalse;
2103
if( outputChannelSelectors ){
2104
// outputChannelSelectors values have already been validated in
2105
// ValidateAsioSpecificStreamInfo() above
2106
info->channelNum = outputChannelSelectors[i];
2108
info->channelNum = i;
2111
info->buffers[0] = info->buffers[1] = 0;
2115
/* Using blocking i/o interface... */
2116
if( usingBlockingIo )
2118
/** @todo REVIEW selection of host buffer size for blocking i/o */
2119
/* Use default host latency for blocking i/o. */
2120
framesPerHostBuffer = SelectHostBufferSize( 0, driverInfo );
2123
else /* Using callback interface... */
2125
framesPerHostBuffer = SelectHostBufferSize(
2126
(( suggestedInputLatencyFrames > suggestedOutputLatencyFrames )
2127
? suggestedInputLatencyFrames : suggestedOutputLatencyFrames),
2132
PA_DEBUG(("PaAsioOpenStream: framesPerHostBuffer :%d\n", framesPerHostBuffer));
2134
asioError = ASIOCreateBuffers( stream->asioBufferInfos,
2135
inputChannelCount+outputChannelCount,
2136
framesPerHostBuffer, &asioCallbacks_ );
2138
if( asioError != ASE_OK
2139
&& framesPerHostBuffer != (unsigned long)driverInfo->bufferPreferredSize )
2141
PA_DEBUG(("ERROR: ASIOCreateBuffers: %s\n", PaAsio_GetAsioErrorText(asioError) ));
2143
Some buggy drivers (like the Hoontech DSP24) give incorrect
2144
[min, preferred, max] values They should work with the preferred size
2145
value, thus if Pa_ASIO_CreateBuffers fails with the hostBufferSize
2146
computed in SelectHostBufferSize, we try again with the preferred size.
2149
framesPerHostBuffer = driverInfo->bufferPreferredSize;
2151
PA_DEBUG(("PaAsioOpenStream: CORRECTED framesPerHostBuffer :%d\n", framesPerHostBuffer));
2153
ASIOError asioError2 = ASIOCreateBuffers( stream->asioBufferInfos,
2154
inputChannelCount+outputChannelCount,
2155
framesPerHostBuffer, &asioCallbacks_ );
2156
if( asioError2 == ASE_OK )
2160
if( asioError != ASE_OK )
2162
result = paUnanticipatedHostError;
2163
PA_ASIO_SET_LAST_ASIO_ERROR( asioError );
2164
PA_DEBUG(("OpenStream ERROR9\n"));
2168
asioBuffersCreated = 1;
2170
stream->asioChannelInfos = (ASIOChannelInfo*)PaUtil_AllocateMemory(
2171
sizeof(ASIOChannelInfo) * (inputChannelCount + outputChannelCount) );
2172
if( !stream->asioChannelInfos )
2174
result = paInsufficientMemory;
2175
PA_DEBUG(("OpenStream ERROR10\n"));
2179
for( i=0; i < inputChannelCount + outputChannelCount; ++i )
2181
stream->asioChannelInfos[i].channel = stream->asioBufferInfos[i].channelNum;
2182
stream->asioChannelInfos[i].isInput = stream->asioBufferInfos[i].isInput;
2183
asioError = ASIOGetChannelInfo( &stream->asioChannelInfos[i] );
2184
if( asioError != ASE_OK )
2186
result = paUnanticipatedHostError;
2187
PA_ASIO_SET_LAST_ASIO_ERROR( asioError );
2188
PA_DEBUG(("OpenStream ERROR11\n"));
2193
stream->bufferPtrs = (void**)PaUtil_AllocateMemory(
2194
2 * sizeof(void*) * (inputChannelCount + outputChannelCount) );
2195
if( !stream->bufferPtrs )
2197
result = paInsufficientMemory;
2198
PA_DEBUG(("OpenStream ERROR12\n"));
2202
if( inputChannelCount > 0 )
2204
stream->inputBufferPtrs[0] = stream-> bufferPtrs;
2205
stream->inputBufferPtrs[1] = &stream->bufferPtrs[inputChannelCount];
2207
for( i=0; i<inputChannelCount; ++i )
2209
stream->inputBufferPtrs[0][i] = stream->asioBufferInfos[i].buffers[0];
2210
stream->inputBufferPtrs[1][i] = stream->asioBufferInfos[i].buffers[1];
2215
stream->inputBufferPtrs[0] = 0;
2216
stream->inputBufferPtrs[1] = 0;
2219
if( outputChannelCount > 0 )
2221
stream->outputBufferPtrs[0] = &stream->bufferPtrs[inputChannelCount*2];
2222
stream->outputBufferPtrs[1] = &stream->bufferPtrs[inputChannelCount*2 + outputChannelCount];
2224
for( i=0; i<outputChannelCount; ++i )
2226
stream->outputBufferPtrs[0][i] = stream->asioBufferInfos[inputChannelCount+i].buffers[0];
2227
stream->outputBufferPtrs[1][i] = stream->asioBufferInfos[inputChannelCount+i].buffers[1];
2232
stream->outputBufferPtrs[0] = 0;
2233
stream->outputBufferPtrs[1] = 0;
2236
if( inputChannelCount > 0 )
2238
/* FIXME: assume all channels use the same type for now */
2239
ASIOSampleType inputType = stream->asioChannelInfos[0].type;
2241
PA_DEBUG(("ASIO Input type:%d",inputType));
2242
AsioSampleTypeLOG(inputType);
2243
hostInputSampleFormat = AsioSampleTypeToPaNativeSampleFormat( inputType );
2245
SelectAsioToPaConverter( inputType, &stream->inputBufferConverter, &stream->inputShift );
2249
hostInputSampleFormat = 0;
2250
stream->inputBufferConverter = 0;
2253
if( outputChannelCount > 0 )
2255
/* FIXME: assume all channels use the same type for now */
2256
ASIOSampleType outputType = stream->asioChannelInfos[inputChannelCount].type;
2258
PA_DEBUG(("ASIO Output type:%d",outputType));
2259
AsioSampleTypeLOG(outputType);
2260
hostOutputSampleFormat = AsioSampleTypeToPaNativeSampleFormat( outputType );
2262
SelectPaToAsioConverter( outputType, &stream->outputBufferConverter, &stream->outputShift );
2266
hostOutputSampleFormat = 0;
2267
stream->outputBufferConverter = 0;
2271
ASIOGetLatencies( &stream->inputLatency, &stream->outputLatency );
2274
/* Using blocking i/o interface... */
2275
if( usingBlockingIo )
2277
/* Allocate the blocking i/o input ring buffer memory. */
2278
stream->blockingState = (PaAsioStreamBlockingState*)PaUtil_AllocateMemory( sizeof(PaAsioStreamBlockingState) );
2279
if( !stream->blockingState )
2281
result = paInsufficientMemory;
2282
PA_DEBUG(("ERROR! Blocking i/o interface struct allocation failed in OpenStream()\n"));
2286
/* Initialize blocking i/o interface struct. */
2287
stream->blockingState->readFramesReadyEvent = NULL; /* Uninitialized, yet. */
2288
stream->blockingState->writeBuffersReadyEvent = NULL; /* Uninitialized, yet. */
2289
stream->blockingState->readRingBufferData = NULL; /* Uninitialized, yet. */
2290
stream->blockingState->writeRingBufferData = NULL; /* Uninitialized, yet. */
2291
stream->blockingState->readStreamBuffer = NULL; /* Uninitialized, yet. */
2292
stream->blockingState->writeStreamBuffer = NULL; /* Uninitialized, yet. */
2293
stream->blockingState->stopFlag = TRUE; /* Not started, yet. */
2296
/* If the user buffer is unspecified */
2297
if( framesPerBuffer == paFramesPerBufferUnspecified )
2299
/* Make the user buffer the same size as the host buffer. */
2300
framesPerBuffer = framesPerHostBuffer;
2304
/* Initialize callback buffer processor. */
2305
result = PaUtil_InitializeBufferProcessor( &stream->bufferProcessor ,
2307
inputSampleFormat & ~paNonInterleaved , /* Ring buffer. */
2308
hostInputSampleFormat , /* Host format. */
2309
outputChannelCount ,
2310
outputSampleFormat & ~paNonInterleaved, /* Ring buffer. */
2311
hostOutputSampleFormat , /* Host format. */
2314
framesPerBuffer , /* Frames per ring buffer block. */
2315
framesPerHostBuffer , /* Frames per asio buffer. */
2316
paUtilFixedHostBufferSize ,
2319
if( result != paNoError ){
2320
PA_DEBUG(("OpenStream ERROR13\n"));
2323
callbackBufferProcessorInited = TRUE;
2325
/* Initialize the blocking i/o buffer processor. */
2326
result = PaUtil_InitializeBufferProcessor(&stream->blockingState->bufferProcessor,
2328
inputSampleFormat , /* User format. */
2329
inputSampleFormat & ~paNonInterleaved , /* Ring buffer. */
2330
outputChannelCount ,
2331
outputSampleFormat , /* User format. */
2332
outputSampleFormat & ~paNonInterleaved, /* Ring buffer. */
2334
paClipOff | paDitherOff , /* Don't use dither nor clipping. */
2335
framesPerBuffer , /* Frames per user buffer. */
2336
framesPerBuffer , /* Frames per ring buffer block. */
2337
paUtilBoundedHostBufferSize ,
2338
NULL, NULL );/* No callback! */
2339
if( result != paNoError ){
2340
PA_DEBUG(("ERROR! Blocking i/o buffer processor initialization failed in OpenStream()\n"));
2343
blockingBufferProcessorInited = TRUE;
2345
/* If input is requested. */
2346
if( inputChannelCount )
2348
/* Create the callback sync-event. */
2349
stream->blockingState->readFramesReadyEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
2350
if( stream->blockingState->readFramesReadyEvent == NULL )
2352
result = paUnanticipatedHostError;
2353
PA_ASIO_SET_LAST_SYSTEM_ERROR( GetLastError() );
2354
PA_DEBUG(("ERROR! Blocking i/o \"read frames ready\" event creation failed in OpenStream()\n"));
2357
blockingReadFramesReadyEventInitialized = 1;
2360
/* Create pointer buffer to access non-interleaved data in ReadStream() */
2361
stream->blockingState->readStreamBuffer = (void**)PaUtil_AllocateMemory( sizeof(void*) * inputChannelCount );
2362
if( !stream->blockingState->readStreamBuffer )
2364
result = paInsufficientMemory;
2365
PA_DEBUG(("ERROR! Blocking i/o read stream buffer allocation failed in OpenStream()\n"));
2369
/* The ring buffer should store as many data blocks as needed
2370
to achieve the requested latency. Whereas it must be large
2371
enough to store at least two complete data blocks.
2373
1) Determine the amount of latency to be added to the
2374
prefered ASIO latency.
2375
2) Make sure we have at lest one additional latency frame.
2376
3) Divide the number of frames by the desired block size to
2377
get the number (rounded up to pure integer) of blocks to
2378
be stored in the buffer.
2379
4) Add one additional block for block processing and convert
2381
5) Get the next larger (or equal) power-of-two buffer size.
2383
lBlockingBufferSize = suggestedInputLatencyFrames - stream->inputLatency;
2384
lBlockingBufferSize = (lBlockingBufferSize > 0) ? lBlockingBufferSize : 1;
2385
lBlockingBufferSize = (lBlockingBufferSize + framesPerBuffer - 1) / framesPerBuffer;
2386
lBlockingBufferSize = (lBlockingBufferSize + 1) * framesPerBuffer;
2388
/* Get the next larger or equal power-of-two buffersize. */
2389
lBlockingBufferSizePow2 = 1;
2390
while( lBlockingBufferSize > (lBlockingBufferSizePow2<<=1) );
2391
lBlockingBufferSize = lBlockingBufferSizePow2;
2393
/* Compute total intput latency in seconds */
2394
stream->streamRepresentation.streamInfo.inputLatency =
2395
(double)( PaUtil_GetBufferProcessorInputLatency(&stream->bufferProcessor )
2396
+ PaUtil_GetBufferProcessorInputLatency(&stream->blockingState->bufferProcessor)
2397
+ (lBlockingBufferSize / framesPerBuffer - 1) * framesPerBuffer
2398
+ stream->inputLatency )
2401
/* The code below prints the ASIO latency which doesn't include
2402
the buffer processor latency nor the blocking i/o latency. It
2403
reports the added latency separately.
2405
PA_DEBUG(("PaAsio : ASIO InputLatency = %ld (%ld ms),\n added buffProc:%ld (%ld ms),\n added blocking:%ld (%ld ms)\n",
2406
stream->inputLatency,
2407
(long)( stream->inputLatency * (1000.0 / sampleRate) ),
2408
PaUtil_GetBufferProcessorInputLatency(&stream->bufferProcessor),
2409
(long)( PaUtil_GetBufferProcessorInputLatency(&stream->bufferProcessor) * (1000.0 / sampleRate) ),
2410
PaUtil_GetBufferProcessorInputLatency(&stream->blockingState->bufferProcessor) + (lBlockingBufferSize / framesPerBuffer - 1) * framesPerBuffer,
2411
(long)( (PaUtil_GetBufferProcessorInputLatency(&stream->blockingState->bufferProcessor) + (lBlockingBufferSize / framesPerBuffer - 1) * framesPerBuffer) * (1000.0 / sampleRate) )
2414
/* Determine the size of ring buffer in bytes. */
2415
lBytesPerFrame = inputChannelCount * Pa_GetSampleSize(inputSampleFormat );
2417
/* Allocate the blocking i/o input ring buffer memory. */
2418
stream->blockingState->readRingBufferData = (void*)PaUtil_AllocateMemory( lBlockingBufferSize * lBytesPerFrame );
2419
if( !stream->blockingState->readRingBufferData )
2421
result = paInsufficientMemory;
2422
PA_DEBUG(("ERROR! Blocking i/o input ring buffer allocation failed in OpenStream()\n"));
2426
/* Initialize the input ring buffer struct. */
2427
PaUtil_InitializeRingBuffer( &stream->blockingState->readRingBuffer ,
2429
lBlockingBufferSize ,
2430
stream->blockingState->readRingBufferData );
2433
/* If output is requested. */
2434
if( outputChannelCount )
2436
stream->blockingState->writeBuffersReadyEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
2437
if( stream->blockingState->writeBuffersReadyEvent == NULL )
2439
result = paUnanticipatedHostError;
2440
PA_ASIO_SET_LAST_SYSTEM_ERROR( GetLastError() );
2441
PA_DEBUG(("ERROR! Blocking i/o \"write buffers ready\" event creation failed in OpenStream()\n"));
2444
blockingWriteBuffersReadyEventInitialized = 1;
2446
/* Create pointer buffer to access non-interleaved data in WriteStream() */
2447
stream->blockingState->writeStreamBuffer = (const void**)PaUtil_AllocateMemory( sizeof(const void*) * outputChannelCount );
2448
if( !stream->blockingState->writeStreamBuffer )
2450
result = paInsufficientMemory;
2451
PA_DEBUG(("ERROR! Blocking i/o write stream buffer allocation failed in OpenStream()\n"));
2455
/* The ring buffer should store as many data blocks as needed
2456
to achieve the requested latency. Whereas it must be large
2457
enough to store at least two complete data blocks.
2459
1) Determine the amount of latency to be added to the
2460
prefered ASIO latency.
2461
2) Make sure we have at lest one additional latency frame.
2462
3) Divide the number of frames by the desired block size to
2463
get the number (rounded up to pure integer) of blocks to
2464
be stored in the buffer.
2465
4) Add one additional block for block processing and convert
2467
5) Get the next larger (or equal) power-of-two buffer size.
2469
lBlockingBufferSize = suggestedOutputLatencyFrames - stream->outputLatency;
2470
lBlockingBufferSize = (lBlockingBufferSize > 0) ? lBlockingBufferSize : 1;
2471
lBlockingBufferSize = (lBlockingBufferSize + framesPerBuffer - 1) / framesPerBuffer;
2472
lBlockingBufferSize = (lBlockingBufferSize + 1) * framesPerBuffer;
2474
/* The buffer size (without the additional block) corresponds
2475
to the initial number of silent samples in the output ring
2477
stream->blockingState->writeRingBufferInitialFrames = lBlockingBufferSize - framesPerBuffer;
2479
/* Get the next larger or equal power-of-two buffersize. */
2480
lBlockingBufferSizePow2 = 1;
2481
while( lBlockingBufferSize > (lBlockingBufferSizePow2<<=1) );
2482
lBlockingBufferSize = lBlockingBufferSizePow2;
2484
/* Compute total output latency in seconds */
2485
stream->streamRepresentation.streamInfo.outputLatency =
2486
(double)( PaUtil_GetBufferProcessorOutputLatency(&stream->bufferProcessor )
2487
+ PaUtil_GetBufferProcessorOutputLatency(&stream->blockingState->bufferProcessor)
2488
+ (lBlockingBufferSize / framesPerBuffer - 1) * framesPerBuffer
2489
+ stream->outputLatency )
2492
/* The code below prints the ASIO latency which doesn't include
2493
the buffer processor latency nor the blocking i/o latency. It
2494
reports the added latency separately.
2496
PA_DEBUG(("PaAsio : ASIO OutputLatency = %ld (%ld ms),\n added buffProc:%ld (%ld ms),\n added blocking:%ld (%ld ms)\n",
2497
stream->outputLatency,
2498
(long)( stream->inputLatency * (1000.0 / sampleRate) ),
2499
PaUtil_GetBufferProcessorOutputLatency(&stream->bufferProcessor),
2500
(long)( PaUtil_GetBufferProcessorOutputLatency(&stream->bufferProcessor) * (1000.0 / sampleRate) ),
2501
PaUtil_GetBufferProcessorOutputLatency(&stream->blockingState->bufferProcessor) + (lBlockingBufferSize / framesPerBuffer - 1) * framesPerBuffer,
2502
(long)( (PaUtil_GetBufferProcessorOutputLatency(&stream->blockingState->bufferProcessor) + (lBlockingBufferSize / framesPerBuffer - 1) * framesPerBuffer) * (1000.0 / sampleRate) )
2505
/* Determine the size of ring buffer in bytes. */
2506
lBytesPerFrame = outputChannelCount * Pa_GetSampleSize(outputSampleFormat);
2508
/* Allocate the blocking i/o output ring buffer memory. */
2509
stream->blockingState->writeRingBufferData = (void*)PaUtil_AllocateMemory( lBlockingBufferSize * lBytesPerFrame );
2510
if( !stream->blockingState->writeRingBufferData )
2512
result = paInsufficientMemory;
2513
PA_DEBUG(("ERROR! Blocking i/o output ring buffer allocation failed in OpenStream()\n"));
2517
/* Initialize the output ring buffer struct. */
2518
PaUtil_InitializeRingBuffer( &stream->blockingState->writeRingBuffer ,
2520
lBlockingBufferSize ,
2521
stream->blockingState->writeRingBufferData );
2524
stream->streamRepresentation.streamInfo.sampleRate = sampleRate;
2528
else /* Using callback interface... */
2530
result = PaUtil_InitializeBufferProcessor( &stream->bufferProcessor,
2531
inputChannelCount, inputSampleFormat, hostInputSampleFormat,
2532
outputChannelCount, outputSampleFormat, hostOutputSampleFormat,
2533
sampleRate, streamFlags, framesPerBuffer,
2534
framesPerHostBuffer, paUtilFixedHostBufferSize,
2535
streamCallback, userData );
2536
if( result != paNoError ){
2537
PA_DEBUG(("OpenStream ERROR13\n"));
2540
callbackBufferProcessorInited = TRUE;
2542
stream->streamRepresentation.streamInfo.inputLatency =
2543
(double)( PaUtil_GetBufferProcessorInputLatency(&stream->bufferProcessor)
2544
+ stream->inputLatency) / sampleRate; // seconds
2545
stream->streamRepresentation.streamInfo.outputLatency =
2546
(double)( PaUtil_GetBufferProcessorOutputLatency(&stream->bufferProcessor)
2547
+ stream->outputLatency) / sampleRate; // seconds
2548
stream->streamRepresentation.streamInfo.sampleRate = sampleRate;
2550
// the code below prints the ASIO latency which doesn't include the
2551
// buffer processor latency. it reports the added latency separately
2552
PA_DEBUG(("PaAsio : ASIO InputLatency = %ld (%ld ms), added buffProc:%ld (%ld ms)\n",
2553
stream->inputLatency,
2554
(long)((stream->inputLatency*1000)/ sampleRate),
2555
PaUtil_GetBufferProcessorInputLatency(&stream->bufferProcessor),
2556
(long)((PaUtil_GetBufferProcessorInputLatency(&stream->bufferProcessor)*1000)/ sampleRate)
2559
PA_DEBUG(("PaAsio : ASIO OuputLatency = %ld (%ld ms), added buffProc:%ld (%ld ms)\n",
2560
stream->outputLatency,
2561
(long)((stream->outputLatency*1000)/ sampleRate),
2562
PaUtil_GetBufferProcessorOutputLatency(&stream->bufferProcessor),
2563
(long)((PaUtil_GetBufferProcessorOutputLatency(&stream->bufferProcessor)*1000)/ sampleRate)
2567
stream->asioHostApi = asioHostApi;
2568
stream->framesPerHostCallback = framesPerHostBuffer;
2570
stream->inputChannelCount = inputChannelCount;
2571
stream->outputChannelCount = outputChannelCount;
2572
stream->postOutput = driverInfo->postOutput;
2573
stream->isStopped = 1;
2574
stream->isActive = 0;
2576
asioHostApi->openAsioDeviceIndex = asioDeviceIndex;
2578
theAsioStream = stream;
2579
*s = (PaStream*)stream;
2584
PA_DEBUG(("goto errored\n"));
2587
if( stream->blockingState )
2589
if( blockingBufferProcessorInited )
2590
PaUtil_TerminateBufferProcessor( &stream->blockingState->bufferProcessor );
2592
if( stream->blockingState->writeRingBufferData )
2593
PaUtil_FreeMemory( stream->blockingState->writeRingBufferData );
2594
if( stream->blockingState->writeStreamBuffer )
2595
PaUtil_FreeMemory( stream->blockingState->writeStreamBuffer );
2596
if( blockingWriteBuffersReadyEventInitialized )
2597
CloseHandle( stream->blockingState->writeBuffersReadyEvent );
2599
if( stream->blockingState->readRingBufferData )
2600
PaUtil_FreeMemory( stream->blockingState->readRingBufferData );
2601
if( stream->blockingState->readStreamBuffer )
2602
PaUtil_FreeMemory( stream->blockingState->readStreamBuffer );
2603
if( blockingReadFramesReadyEventInitialized )
2604
CloseHandle( stream->blockingState->readFramesReadyEvent );
2606
PaUtil_FreeMemory( stream->blockingState );
2609
if( callbackBufferProcessorInited )
2610
PaUtil_TerminateBufferProcessor( &stream->bufferProcessor );
2612
if( completedBuffersPlayedEventInited )
2613
CloseHandle( stream->completedBuffersPlayedEvent );
2615
if( stream->asioBufferInfos )
2616
PaUtil_FreeMemory( stream->asioBufferInfos );
2618
if( stream->asioChannelInfos )
2619
PaUtil_FreeMemory( stream->asioChannelInfos );
2621
if( stream->bufferPtrs )
2622
PaUtil_FreeMemory( stream->bufferPtrs );
2624
PaUtil_FreeMemory( stream );
2627
if( asioBuffersCreated )
2628
ASIODisposeBuffers();
2630
if( asioIsInitialized )
2639
When CloseStream() is called, the multi-api layer ensures that
2640
the stream has already been stopped or aborted.
2642
static PaError CloseStream( PaStream* s )
2644
PaError result = paNoError;
2645
PaAsioStream *stream = (PaAsioStream*)s;
2649
- additional stream closing + cleanup
2652
PaUtil_TerminateBufferProcessor( &stream->bufferProcessor );
2653
PaUtil_TerminateStreamRepresentation( &stream->streamRepresentation );
2655
stream->asioHostApi->openAsioDeviceIndex = paNoDevice;
2657
CloseHandle( stream->completedBuffersPlayedEvent );
2659
/* Using blocking i/o interface... */
2660
if( stream->blockingState )
2662
PaUtil_TerminateBufferProcessor( &stream->blockingState->bufferProcessor );
2664
if( stream->inputChannelCount ) {
2665
PaUtil_FreeMemory( stream->blockingState->readRingBufferData );
2666
PaUtil_FreeMemory( stream->blockingState->readStreamBuffer );
2667
CloseHandle( stream->blockingState->readFramesReadyEvent );
2669
if( stream->outputChannelCount ) {
2670
PaUtil_FreeMemory( stream->blockingState->writeRingBufferData );
2671
PaUtil_FreeMemory( stream->blockingState->writeStreamBuffer );
2672
CloseHandle( stream->blockingState->writeBuffersReadyEvent );
2675
PaUtil_FreeMemory( stream->blockingState );
2678
PaUtil_FreeMemory( stream->asioBufferInfos );
2679
PaUtil_FreeMemory( stream->asioChannelInfos );
2680
PaUtil_FreeMemory( stream->bufferPtrs );
2681
PaUtil_FreeMemory( stream );
2683
ASIODisposeBuffers();
2692
static void bufferSwitch(long index, ASIOBool directProcess)
2694
//TAKEN FROM THE ASIO SDK
2696
// the actual processing callback.
2697
// Beware that this is normally in a seperate thread, hence be sure that
2698
// you take care about thread synchronization. This is omitted here for
2701
// as this is a "back door" into the bufferSwitchTimeInfo a timeInfo needs
2702
// to be created though it will only set the timeInfo.samplePosition and
2703
// timeInfo.systemTime fields and the according flags
2706
memset( &timeInfo, 0, sizeof (timeInfo) );
2708
// get the time stamp of the buffer, not necessary if no
2709
// synchronization to other media is required
2710
if( ASIOGetSamplePosition(&timeInfo.timeInfo.samplePosition, &timeInfo.timeInfo.systemTime) == ASE_OK)
2711
timeInfo.timeInfo.flags = kSystemTimeValid | kSamplePositionValid;
2713
// Call the real callback
2714
bufferSwitchTimeInfo( &timeInfo, index, directProcess );
2718
// conversion from 64 bit ASIOSample/ASIOTimeStamp to double float
2720
#define ASIO64toDouble(a) (a)
2722
const double twoRaisedTo32 = 4294967296.;
2723
#define ASIO64toDouble(a) ((a).lo + (a).hi * twoRaisedTo32)
2726
static ASIOTime *bufferSwitchTimeInfo( ASIOTime *timeInfo, long index, ASIOBool directProcess )
2728
// the actual processing callback.
2729
// Beware that this is normally in a seperate thread, hence be sure that
2730
// you take care about thread synchronization.
2733
/* The SDK says the following about the directProcess flag:
2734
suggests to the host whether it should immediately start processing
2735
(directProcess == ASIOTrue), or whether its process should be deferred
2736
because the call comes from a very low level (for instance, a high level
2737
priority interrupt), and direct processing would cause timing instabilities for
2738
the rest of the system. If in doubt, directProcess should be set to ASIOFalse.
2740
We just ignore directProcess. This could cause incompatibilities with
2741
drivers which really don't want the audio processing to occur in this
2742
callback, but none have been identified yet.
2745
(void) directProcess; /* suppress unused parameter warning */
2748
// store the timeInfo for later use
2749
asioDriverInfo.tInfo = *timeInfo;
2751
// get the time stamp of the buffer, not necessary if no
2752
// synchronization to other media is required
2754
if (timeInfo->timeInfo.flags & kSystemTimeValid)
2755
asioDriverInfo.nanoSeconds = ASIO64toDouble(timeInfo->timeInfo.systemTime);
2757
asioDriverInfo.nanoSeconds = 0;
2759
if (timeInfo->timeInfo.flags & kSamplePositionValid)
2760
asioDriverInfo.samples = ASIO64toDouble(timeInfo->timeInfo.samplePosition);
2762
asioDriverInfo.samples = 0;
2764
if (timeInfo->timeCode.flags & kTcValid)
2765
asioDriverInfo.tcSamples = ASIO64toDouble(timeInfo->timeCode.timeCodeSamples);
2767
asioDriverInfo.tcSamples = 0;
2769
// get the system reference time
2770
asioDriverInfo.sysRefTime = get_sys_reference_time();
2774
// a few debug messages for the Windows device driver developer
2775
// tells you the time when driver got its interrupt and the delay until the app receives
2776
// the event notification.
2777
static double last_samples = 0;
2779
sprintf (tmp, "diff: %d / %d ms / %d ms / %d samples \n", asioDriverInfo.sysRefTime - (long)(asioDriverInfo.nanoSeconds / 1000000.0), asioDriverInfo.sysRefTime, (long)(asioDriverInfo.nanoSeconds / 1000000.0), (long)(asioDriverInfo.samples - last_samples));
2780
OutputDebugString (tmp);
2781
last_samples = asioDriverInfo.samples;
2785
if( !theAsioStream )
2788
// Keep sample position
2789
// FIXME: asioDriverInfo.pahsc_NumFramesDone = timeInfo->timeInfo.samplePosition.lo;
2792
// protect against reentrancy
2793
if( PaAsio_AtomicIncrement(&theAsioStream->reenterCount) )
2795
theAsioStream->reenterError++;
2796
//DBUG(("bufferSwitchTimeInfo : reentrancy detection = %d\n", asioDriverInfo.reenterError));
2800
int buffersDone = 0;
2804
if( buffersDone > 0 )
2806
// this is a reentered buffer, we missed processing it on time
2807
// set the input overflow and output underflow flags as appropriate
2809
if( theAsioStream->inputChannelCount > 0 )
2810
theAsioStream->callbackFlags |= paInputOverflow;
2812
if( theAsioStream->outputChannelCount > 0 )
2813
theAsioStream->callbackFlags |= paOutputUnderflow;
2817
if( theAsioStream->zeroOutput )
2819
ZeroOutputBuffers( theAsioStream, index );
2821
// Finally if the driver supports the ASIOOutputReady() optimization,
2822
// do it here, all data are in place
2823
if( theAsioStream->postOutput )
2826
if( theAsioStream->stopProcessing )
2828
if( theAsioStream->stopPlayoutCount < 2 )
2830
++theAsioStream->stopPlayoutCount;
2831
if( theAsioStream->stopPlayoutCount == 2 )
2833
theAsioStream->isActive = 0;
2834
if( theAsioStream->streamRepresentation.streamFinishedCallback != 0 )
2835
theAsioStream->streamRepresentation.streamFinishedCallback( theAsioStream->streamRepresentation.userData );
2836
theAsioStream->streamFinishedCallbackCalled = true;
2837
SetEvent( theAsioStream->completedBuffersPlayedEvent );
2846
// test code to try to detect slip conditions... these may work on some systems
2847
// but neither of them work on the RME Digi96
2849
// check that sample delta matches buffer size (otherwise we must have skipped
2851
static double last_samples = -512;
2853
//if( timeInfo->timeCode.flags & kTcValid )
2854
// samples = ASIO64toDouble(timeInfo->timeCode.timeCodeSamples);
2856
samples = ASIO64toDouble(timeInfo->timeInfo.samplePosition);
2857
int delta = samples - last_samples;
2858
//printf( "%d\n", delta);
2859
last_samples = samples;
2861
if( delta > theAsioStream->framesPerHostCallback )
2863
if( theAsioStream->inputChannelCount > 0 )
2864
theAsioStream->callbackFlags |= paInputOverflow;
2866
if( theAsioStream->outputChannelCount > 0 )
2867
theAsioStream->callbackFlags |= paOutputUnderflow;
2870
// check that the buffer index is not the previous index (which would indicate
2871
// that a buffer was skipped.
2872
static int previousIndex = 1;
2873
if( index == previousIndex )
2875
if( theAsioStream->inputChannelCount > 0 )
2876
theAsioStream->callbackFlags |= paInputOverflow;
2878
if( theAsioStream->outputChannelCount > 0 )
2879
theAsioStream->callbackFlags |= paOutputUnderflow;
2881
previousIndex = index;
2886
PaUtil_BeginCpuLoadMeasurement( &theAsioStream->cpuLoadMeasurer );
2888
PaStreamCallbackTimeInfo paTimeInfo;
2890
// asio systemTime is supposed to be measured according to the same
2891
// clock as timeGetTime
2892
paTimeInfo.currentTime = (ASIO64toDouble( timeInfo->timeInfo.systemTime ) * .000000001);
2894
/* patch from Paul Boege */
2895
paTimeInfo.inputBufferAdcTime = paTimeInfo.currentTime -
2896
((double)theAsioStream->inputLatency/theAsioStream->streamRepresentation.streamInfo.sampleRate);
2898
paTimeInfo.outputBufferDacTime = paTimeInfo.currentTime +
2899
((double)theAsioStream->outputLatency/theAsioStream->streamRepresentation.streamInfo.sampleRate);
2901
/* old version is buggy because the buffer processor also adds in its latency to the time parameters
2902
paTimeInfo.inputBufferAdcTime = paTimeInfo.currentTime - theAsioStream->streamRepresentation.streamInfo.inputLatency;
2903
paTimeInfo.outputBufferDacTime = paTimeInfo.currentTime + theAsioStream->streamRepresentation.streamInfo.outputLatency;
2906
/* Disabled! Stopping and re-starting the stream causes an input overflow / output undeflow. S.Fischer */
2908
// detect underflows by checking inter-callback time > 2 buffer period
2909
static double previousTime = -1;
2910
if( previousTime > 0 ){
2912
double delta = paTimeInfo.currentTime - previousTime;
2914
if( delta >= 2. * (theAsioStream->framesPerHostCallback / theAsioStream->streamRepresentation.streamInfo.sampleRate) ){
2915
if( theAsioStream->inputChannelCount > 0 )
2916
theAsioStream->callbackFlags |= paInputOverflow;
2918
if( theAsioStream->outputChannelCount > 0 )
2919
theAsioStream->callbackFlags |= paOutputUnderflow;
2922
previousTime = paTimeInfo.currentTime;
2925
// note that the above input and output times do not need to be
2926
// adjusted for the latency of the buffer processor -- the buffer
2927
// processor handles that.
2929
if( theAsioStream->inputBufferConverter )
2931
for( i=0; i<theAsioStream->inputChannelCount; i++ )
2933
theAsioStream->inputBufferConverter( theAsioStream->inputBufferPtrs[index][i],
2934
theAsioStream->inputShift, theAsioStream->framesPerHostCallback );
2938
PaUtil_BeginBufferProcessing( &theAsioStream->bufferProcessor, &paTimeInfo, theAsioStream->callbackFlags );
2940
/* reset status flags once they've been passed to the callback */
2941
theAsioStream->callbackFlags = 0;
2943
PaUtil_SetInputFrameCount( &theAsioStream->bufferProcessor, 0 /* default to host buffer size */ );
2944
for( i=0; i<theAsioStream->inputChannelCount; ++i )
2945
PaUtil_SetNonInterleavedInputChannel( &theAsioStream->bufferProcessor, i, theAsioStream->inputBufferPtrs[index][i] );
2947
PaUtil_SetOutputFrameCount( &theAsioStream->bufferProcessor, 0 /* default to host buffer size */ );
2948
for( i=0; i<theAsioStream->outputChannelCount; ++i )
2949
PaUtil_SetNonInterleavedOutputChannel( &theAsioStream->bufferProcessor, i, theAsioStream->outputBufferPtrs[index][i] );
2952
if( theAsioStream->stopProcessing )
2953
callbackResult = paComplete;
2955
callbackResult = paContinue;
2956
unsigned long framesProcessed = PaUtil_EndBufferProcessing( &theAsioStream->bufferProcessor, &callbackResult );
2958
if( theAsioStream->outputBufferConverter )
2960
for( i=0; i<theAsioStream->outputChannelCount; i++ )
2962
theAsioStream->outputBufferConverter( theAsioStream->outputBufferPtrs[index][i],
2963
theAsioStream->outputShift, theAsioStream->framesPerHostCallback );
2967
PaUtil_EndCpuLoadMeasurement( &theAsioStream->cpuLoadMeasurer, framesProcessed );
2969
// Finally if the driver supports the ASIOOutputReady() optimization,
2970
// do it here, all data are in place
2971
if( theAsioStream->postOutput )
2974
if( callbackResult == paContinue )
2976
/* nothing special to do */
2978
else if( callbackResult == paAbort )
2980
/* finish playback immediately */
2981
theAsioStream->isActive = 0;
2982
if( theAsioStream->streamRepresentation.streamFinishedCallback != 0 )
2983
theAsioStream->streamRepresentation.streamFinishedCallback( theAsioStream->streamRepresentation.userData );
2984
theAsioStream->streamFinishedCallbackCalled = true;
2985
SetEvent( theAsioStream->completedBuffersPlayedEvent );
2986
theAsioStream->zeroOutput = true;
2988
else /* paComplete or other non-zero value indicating complete */
2990
/* Finish playback once currently queued audio has completed. */
2991
theAsioStream->stopProcessing = true;
2993
if( PaUtil_IsBufferProcessorOutputEmpty( &theAsioStream->bufferProcessor ) )
2995
theAsioStream->zeroOutput = true;
2996
theAsioStream->stopPlayoutCount = 0;
3003
}while( PaAsio_AtomicDecrement(&theAsioStream->reenterCount) >= 0 );
3009
static void sampleRateChanged(ASIOSampleRate sRate)
3011
// TAKEN FROM THE ASIO SDK
3012
// do whatever you need to do if the sample rate changed
3013
// usually this only happens during external sync.
3014
// Audio processing is not stopped by the driver, actual sample rate
3015
// might not have even changed, maybe only the sample rate status of an
3016
// AES/EBU or S/PDIF digital input at the audio device.
3017
// You might have to update time/sample related conversion routines, etc.
3019
(void) sRate; /* unused parameter */
3020
PA_DEBUG( ("sampleRateChanged : %d \n", sRate));
3023
static long asioMessages(long selector, long value, void* message, double* opt)
3025
// TAKEN FROM THE ASIO SDK
3026
// currently the parameters "value", "message" and "opt" are not used.
3029
(void) message; /* unused parameters */
3032
PA_DEBUG( ("asioMessages : %d , %d \n", selector, value));
3036
case kAsioSelectorSupported:
3037
if(value == kAsioResetRequest
3038
|| value == kAsioEngineVersion
3039
|| value == kAsioResyncRequest
3040
|| value == kAsioLatenciesChanged
3041
// the following three were added for ASIO 2.0, you don't necessarily have to support them
3042
|| value == kAsioSupportsTimeInfo
3043
|| value == kAsioSupportsTimeCode
3044
|| value == kAsioSupportsInputMonitor)
3048
case kAsioBufferSizeChange:
3049
//printf("kAsioBufferSizeChange \n");
3052
case kAsioResetRequest:
3053
// defer the task and perform the reset of the driver during the next "safe" situation
3054
// You cannot reset the driver right now, as this code is called from the driver.
3055
// Reset the driver is done by completely destruct is. I.e. ASIOStop(), ASIODisposeBuffers(), Destruction
3056
// Afterwards you initialize the driver again.
3058
/*FIXME: commented the next line out */
3059
//asioDriverInfo.stopped; // In this sample the processing will just stop
3063
case kAsioResyncRequest:
3064
// This informs the application, that the driver encountered some non fatal data loss.
3065
// It is used for synchronization purposes of different media.
3066
// Added mainly to work around the Win16Mutex problems in Windows 95/98 with the
3067
// Windows Multimedia system, which could loose data because the Mutex was hold too long
3068
// by another thread.
3069
// However a driver can issue it in other situations, too.
3073
case kAsioLatenciesChanged:
3074
// This will inform the host application that the drivers were latencies changed.
3075
// Beware, it this does not mean that the buffer sizes have changed!
3076
// You might need to update internal delay data.
3078
//printf("kAsioLatenciesChanged \n");
3081
case kAsioEngineVersion:
3082
// return the supported ASIO version of the host application
3083
// If a host applications does not implement this selector, ASIO 1.0 is assumed
3088
case kAsioSupportsTimeInfo:
3089
// informs the driver wether the asioCallbacks.bufferSwitchTimeInfo() callback
3091
// For compatibility with ASIO 1.0 drivers the host application should always support
3092
// the "old" bufferSwitch method, too.
3096
case kAsioSupportsTimeCode:
3097
// informs the driver wether application is interested in time code info.
3098
// If an application does not need to know about time code, the driver has less work
3107
static PaError StartStream( PaStream *s )
3109
PaError result = paNoError;
3110
PaAsioStream *stream = (PaAsioStream*)s;
3111
PaAsioStreamBlockingState *blockingState = stream->blockingState;
3112
ASIOError asioError;
3114
if( stream->outputChannelCount > 0 )
3116
ZeroOutputBuffers( stream, 0 );
3117
ZeroOutputBuffers( stream, 1 );
3120
PaUtil_ResetBufferProcessor( &stream->bufferProcessor );
3121
stream->stopProcessing = false;
3122
stream->zeroOutput = false;
3124
/* Reentrancy counter initialisation */
3125
stream->reenterCount = -1;
3126
stream->reenterError = 0;
3128
stream->callbackFlags = 0;
3130
if( ResetEvent( stream->completedBuffersPlayedEvent ) == 0 )
3132
result = paUnanticipatedHostError;
3133
PA_ASIO_SET_LAST_SYSTEM_ERROR( GetLastError() );
3137
/* Using blocking i/o interface... */
3140
/* Reset blocking i/o buffer processor. */
3141
PaUtil_ResetBufferProcessor( &blockingState->bufferProcessor );
3143
/* If we're about to process some input data. */
3144
if( stream->inputChannelCount )
3146
/* Reset callback-ReadStream sync event. */
3147
if( ResetEvent( blockingState->readFramesReadyEvent ) == 0 )
3149
result = paUnanticipatedHostError;
3150
PA_ASIO_SET_LAST_SYSTEM_ERROR( GetLastError() );
3153
/* Flush blocking i/o ring buffer. */
3154
PaUtil_FlushRingBuffer( &blockingState->readRingBuffer );
3155
(*blockingState->bufferProcessor.inputZeroer)( blockingState->readRingBuffer.buffer, 1, blockingState->bufferProcessor.inputChannelCount * blockingState->readRingBuffer.bufferSize );
3158
/* If we're about to process some output data. */
3159
if( stream->outputChannelCount )
3161
/* Reset callback-WriteStream sync event. */
3162
if( ResetEvent( blockingState->writeBuffersReadyEvent ) == 0 )
3164
result = paUnanticipatedHostError;
3165
PA_ASIO_SET_LAST_SYSTEM_ERROR( GetLastError() );
3168
/* Flush blocking i/o ring buffer. */
3169
PaUtil_FlushRingBuffer( &blockingState->writeRingBuffer );
3170
(*blockingState->bufferProcessor.outputZeroer)( blockingState->writeRingBuffer.buffer, 1, blockingState->bufferProcessor.outputChannelCount * blockingState->writeRingBuffer.bufferSize );
3172
/* Initialize the output ring buffer to "silence". */
3173
PaUtil_AdvanceRingBufferWriteIndex( &blockingState->writeRingBuffer, blockingState->writeRingBufferInitialFrames );
3176
/* Clear requested frames / buffers count. */
3177
blockingState->writeBuffersRequested = 0;
3178
blockingState->readFramesRequested = 0;
3179
blockingState->writeBuffersRequestedFlag = FALSE;
3180
blockingState->readFramesRequestedFlag = FALSE;
3181
blockingState->outputUnderflowFlag = FALSE;
3182
blockingState->inputOverflowFlag = FALSE;
3183
blockingState->stopFlag = FALSE;
3187
if( result == paNoError )
3189
assert( theAsioStream == stream ); /* theAsioStream should be set correctly in OpenStream */
3191
/* initialize these variables before the callback has a chance to be invoked */
3192
stream->isStopped = 0;
3193
stream->isActive = 1;
3194
stream->streamFinishedCallbackCalled = false;
3196
asioError = ASIOStart();
3197
if( asioError != ASE_OK )
3199
stream->isStopped = 1;
3200
stream->isActive = 0;
3202
result = paUnanticipatedHostError;
3203
PA_ASIO_SET_LAST_ASIO_ERROR( asioError );
3210
static void EnsureCallbackHasCompleted( PaAsioStream *stream )
3212
// make sure that the callback is not still in-flight after ASIOStop()
3213
// returns. This has been observed to happen on the Hoontech DSP24 for
3215
int count = 2000; // only wait for 2 seconds, rather than hanging.
3216
while( stream->reenterCount != -1 && count > 0 )
3223
static PaError StopStream( PaStream *s )
3225
PaError result = paNoError;
3226
PaAsioStream *stream = (PaAsioStream*)s;
3227
PaAsioStreamBlockingState *blockingState = stream->blockingState;
3228
ASIOError asioError;
3230
if( stream->isActive )
3232
/* If blocking i/o output is in use */
3233
if( blockingState && stream->outputChannelCount )
3235
/* Request the whole output buffer to be available. */
3236
blockingState->writeBuffersRequested = blockingState->writeRingBuffer.bufferSize;
3237
/* Signalize that additional buffers are need. */
3238
blockingState->writeBuffersRequestedFlag = TRUE;
3239
/* Set flag to indicate the playback is to be stopped. */
3240
blockingState->stopFlag = TRUE;
3242
/* Wait until requested number of buffers has been freed. Time
3243
out after twice the blocking i/o ouput buffer could have
3245
DWORD timeout = (DWORD)( 2 * blockingState->writeRingBuffer.bufferSize * 1000
3246
/ stream->streamRepresentation.streamInfo.sampleRate );
3247
DWORD waitResult = WaitForSingleObject( blockingState->writeBuffersReadyEvent, timeout );
3249
/* If something seriously went wrong... */
3250
if( waitResult == WAIT_FAILED )
3252
PA_DEBUG(("WaitForSingleObject() failed in StopStream()\n"));
3253
result = paUnanticipatedHostError;
3254
PA_ASIO_SET_LAST_SYSTEM_ERROR( GetLastError() );
3256
else if( waitResult == WAIT_TIMEOUT )
3258
PA_DEBUG(("WaitForSingleObject() timed out in StopStream()\n"));
3259
result = paTimedOut;
3263
stream->stopProcessing = true;
3265
/* wait for the stream to finish playing out enqueued buffers.
3266
timeout after four times the stream latency.
3268
@todo should use a better time out value - if the user buffer
3269
length is longer than the asio buffer size then that should
3270
be taken into account.
3272
if( WaitForSingleObject( stream->completedBuffersPlayedEvent,
3273
(DWORD)(stream->streamRepresentation.streamInfo.outputLatency * 1000. * 4.) )
3276
PA_DEBUG(("WaitForSingleObject() timed out in StopStream()\n" ));
3280
asioError = ASIOStop();
3281
if( asioError == ASE_OK )
3283
EnsureCallbackHasCompleted( stream );
3287
result = paUnanticipatedHostError;
3288
PA_ASIO_SET_LAST_ASIO_ERROR( asioError );
3291
stream->isStopped = 1;
3292
stream->isActive = 0;
3294
if( !stream->streamFinishedCallbackCalled )
3296
if( stream->streamRepresentation.streamFinishedCallback != 0 )
3297
stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData );
3303
static PaError AbortStream( PaStream *s )
3305
PaError result = paNoError;
3306
PaAsioStream *stream = (PaAsioStream*)s;
3307
ASIOError asioError;
3309
stream->zeroOutput = true;
3311
asioError = ASIOStop();
3312
if( asioError == ASE_OK )
3314
EnsureCallbackHasCompleted( stream );
3318
result = paUnanticipatedHostError;
3319
PA_ASIO_SET_LAST_ASIO_ERROR( asioError );
3322
stream->isStopped = 1;
3323
stream->isActive = 0;
3325
if( !stream->streamFinishedCallbackCalled )
3327
if( stream->streamRepresentation.streamFinishedCallback != 0 )
3328
stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData );
3335
static PaError IsStreamStopped( PaStream *s )
3337
PaAsioStream *stream = (PaAsioStream*)s;
3339
return stream->isStopped;
3343
static PaError IsStreamActive( PaStream *s )
3345
PaAsioStream *stream = (PaAsioStream*)s;
3347
return stream->isActive;
3351
static PaTime GetStreamTime( PaStream *s )
3353
(void) s; /* unused parameter */
3354
return (double)timeGetTime() * .001;
3358
static double GetStreamCpuLoad( PaStream* s )
3360
PaAsioStream *stream = (PaAsioStream*)s;
3362
return PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer );
3367
As separate stream interfaces are used for blocking and callback
3368
streams, the following functions can be guaranteed to only be called
3369
for blocking streams.
3372
static PaError ReadStream( PaStream *s ,
3374
unsigned long frames )
3376
PaError result = paNoError; /* Initial return value. */
3377
PaAsioStream *stream = (PaAsioStream*)s; /* The PA ASIO stream. */
3379
/* Pointer to the blocking i/o data struct. */
3380
PaAsioStreamBlockingState *blockingState = stream->blockingState;
3382
/* Get blocking i/o buffer processor and ring buffer pointers. */
3383
PaUtilBufferProcessor *pBp = &blockingState->bufferProcessor;
3384
PaUtilRingBuffer *pRb = &blockingState->readRingBuffer;
3386
/* Ring buffer segment(s) used for writing. */
3387
void *pRingBufferData1st = NULL; /* First segment. (Mandatory) */
3388
void *pRingBufferData2nd = NULL; /* Second segment. (Optional) */
3390
/* Number of frames per ring buffer segment. */
3391
long lRingBufferSize1st = 0; /* First segment. (Mandatory) */
3392
long lRingBufferSize2nd = 0; /* Second segment. (Optional) */
3394
/* Get number of frames to be processed per data block. */
3395
unsigned long lFramesPerBlock = stream->bufferProcessor.framesPerUserBuffer;
3396
/* Actual number of frames that has been copied into the ring buffer. */
3397
unsigned long lFramesCopied = 0;
3398
/* The number of remaining unprocessed dtat frames. */
3399
unsigned long lFramesRemaining = frames;
3401
/* Copy the input argument to avoid pointer increment! */
3402
const void *userBuffer;
3403
unsigned int i; /* Just a counter. */
3405
/* About the time, needed to process 8 data blocks. */
3406
DWORD timeout = (DWORD)( 8 * lFramesPerBlock * 1000 / stream->streamRepresentation.streamInfo.sampleRate );
3407
DWORD waitResult = 0;
3410
/* Check if the stream is still available ready to gather new data. */
3411
if( blockingState->stopFlag || !stream->isActive )
3413
PA_DEBUG(("Warning! Stream no longer available for reading in ReadStream()\n"));
3414
result = paStreamIsStopped;
3418
/* If the stream is a input stream. */
3419
if( stream->inputChannelCount )
3421
/* Prepare buffer access. */
3422
if( !pBp->userOutputIsInterleaved )
3424
userBuffer = blockingState->readStreamBuffer;
3425
for( i = 0; i<pBp->inputChannelCount; ++i )
3427
((void**)userBuffer)[i] = ((void**)buffer)[i];
3429
} /* Use the unchanged buffer. */
3430
else { userBuffer = buffer; }
3432
do /* Internal block processing for too large user data buffers. */
3434
/* Get the size of the current data block to be processed. */
3435
lFramesPerBlock =(lFramesPerBlock < lFramesRemaining)
3436
? lFramesPerBlock : lFramesRemaining;
3437
/* Use predefined block size for as long there are enough
3438
buffers available, thereafter reduce the processing block
3439
size to match the number of remaining buffers. So the final
3440
data block is processed although it may be incomplete. */
3442
/* If the available amount of data frames is insufficient. */
3443
if( PaUtil_GetRingBufferReadAvailable(pRb) < (long) lFramesPerBlock )
3445
/* Make sure, the event isn't already set! */
3446
/* ResetEvent( blockingState->readFramesReadyEvent ); */
3448
/* Set the number of requested buffers. */
3449
blockingState->readFramesRequested = lFramesPerBlock;
3451
/* Signalize that additional buffers are need. */
3452
blockingState->readFramesRequestedFlag = TRUE;
3454
/* Wait until requested number of buffers has been freed. */
3455
waitResult = WaitForSingleObject( blockingState->readFramesReadyEvent, timeout );
3457
/* If something seriously went wrong... */
3458
if( waitResult == WAIT_FAILED )
3460
PA_DEBUG(("WaitForSingleObject() failed in ReadStream()\n"));
3461
result = paUnanticipatedHostError;
3462
PA_ASIO_SET_LAST_SYSTEM_ERROR( GetLastError() );
3465
else if( waitResult == WAIT_TIMEOUT )
3467
PA_DEBUG(("WaitForSingleObject() timed out in ReadStream()\n"));
3469
/* If block processing has stopped, abort! */
3470
if( blockingState->stopFlag ) { return result = paStreamIsStopped; }
3472
/* If a timeout is encountered, give up eventually. */
3473
return result = paTimedOut;
3476
/* Now, the ring buffer contains the required amount of data
3478
(Therefor we don't need to check the return argument of
3479
PaUtil_GetRingBufferReadRegions(). ;-) )
3482
/* Retrieve pointer(s) to the ring buffer's current write
3483
position(s). If the first buffer segment is too small to
3484
store the requested number of bytes, an additional second
3485
segment is returned. Otherwise, i.e. if the first segment
3486
is large enough, the second segment's pointer will be NULL.
3488
PaUtil_GetRingBufferReadRegions(pRb ,
3490
&pRingBufferData1st,
3491
&lRingBufferSize1st,
3492
&pRingBufferData2nd,
3493
&lRingBufferSize2nd);
3495
/* Set number of frames to be copied from the ring buffer. */
3496
PaUtil_SetInputFrameCount( pBp, lRingBufferSize1st );
3497
/* Setup ring buffer access. */
3498
PaUtil_SetInterleavedInputChannels(pBp , /* Buffer processor. */
3499
0 , /* The first channel's index. */
3500
pRingBufferData1st, /* First ring buffer segment. */
3501
0 ); /* Use all available channels. */
3503
/* If a second ring buffer segment is required. */
3504
if( lRingBufferSize2nd ) {
3505
/* Set number of frames to be copied from the ring buffer. */
3506
PaUtil_Set2ndInputFrameCount( pBp, lRingBufferSize2nd );
3507
/* Setup ring buffer access. */
3508
PaUtil_Set2ndInterleavedInputChannels(pBp , /* Buffer processor. */
3509
0 , /* The first channel's index. */
3510
pRingBufferData2nd, /* Second ring buffer segment. */
3511
0 ); /* Use all available channels. */
3514
/* Let the buffer processor handle "copy and conversion" and
3515
update the ring buffer indices manually. */
3516
lFramesCopied = PaUtil_CopyInput( pBp, &buffer, lFramesPerBlock );
3517
PaUtil_AdvanceRingBufferReadIndex( pRb, lFramesCopied );
3519
/* Decrease number of unprocessed frames. */
3520
lFramesRemaining -= lFramesCopied;
3522
} /* Continue with the next data chunk. */
3523
while( lFramesRemaining );
3526
/* If there has been an input overflow within the callback */
3527
if( blockingState->inputOverflowFlag )
3529
blockingState->inputOverflowFlag = FALSE;
3531
/* Return the corresponding error code. */
3532
result = paInputOverflowed;
3535
} /* If this is not an input stream. */
3537
result = paCanNotReadFromAnOutputOnlyStream;
3543
static PaError WriteStream( PaStream *s ,
3545
unsigned long frames )
3547
PaError result = paNoError; /* Initial return value. */
3548
PaAsioStream *stream = (PaAsioStream*)s; /* The PA ASIO stream. */
3550
/* Pointer to the blocking i/o data struct. */
3551
PaAsioStreamBlockingState *blockingState = stream->blockingState;
3553
/* Get blocking i/o buffer processor and ring buffer pointers. */
3554
PaUtilBufferProcessor *pBp = &blockingState->bufferProcessor;
3555
PaUtilRingBuffer *pRb = &blockingState->writeRingBuffer;
3557
/* Ring buffer segment(s) used for writing. */
3558
void *pRingBufferData1st = NULL; /* First segment. (Mandatory) */
3559
void *pRingBufferData2nd = NULL; /* Second segment. (Optional) */
3561
/* Number of frames per ring buffer segment. */
3562
long lRingBufferSize1st = 0; /* First segment. (Mandatory) */
3563
long lRingBufferSize2nd = 0; /* Second segment. (Optional) */
3565
/* Get number of frames to be processed per data block. */
3566
unsigned long lFramesPerBlock = stream->bufferProcessor.framesPerUserBuffer;
3567
/* Actual number of frames that has been copied into the ring buffer. */
3568
unsigned long lFramesCopied = 0;
3569
/* The number of remaining unprocessed dtat frames. */
3570
unsigned long lFramesRemaining = frames;
3572
/* About the time, needed to process 8 data blocks. */
3573
DWORD timeout = (DWORD)( 8 * lFramesPerBlock * 1000 / stream->streamRepresentation.streamInfo.sampleRate );
3574
DWORD waitResult = 0;
3576
/* Copy the input argument to avoid pointer increment! */
3577
const void *userBuffer;
3578
unsigned int i; /* Just a counter. */
3581
/* Check if the stream ist still available ready to recieve new data. */
3582
if( blockingState->stopFlag || !stream->isActive )
3584
PA_DEBUG(("Warning! Stream no longer available for writing in WriteStream()\n"));
3585
result = paStreamIsStopped;
3589
/* If the stream is a output stream. */
3590
if( stream->outputChannelCount )
3592
/* Prepare buffer access. */
3593
if( !pBp->userOutputIsInterleaved )
3595
userBuffer = blockingState->writeStreamBuffer;
3596
for( i = 0; i<pBp->outputChannelCount; ++i )
3598
((const void**)userBuffer)[i] = ((const void**)buffer)[i];
3600
} /* Use the unchanged buffer. */
3601
else { userBuffer = buffer; }
3604
do /* Internal block processing for too large user data buffers. */
3606
/* Get the size of the current data block to be processed. */
3607
lFramesPerBlock =(lFramesPerBlock < lFramesRemaining)
3608
? lFramesPerBlock : lFramesRemaining;
3609
/* Use predefined block size for as long there are enough
3610
frames available, thereafter reduce the processing block
3611
size to match the number of remaining frames. So the final
3612
data block is processed although it may be incomplete. */
3614
/* If the available amount of buffers is insufficient. */
3615
if( PaUtil_GetRingBufferWriteAvailable(pRb) < (long) lFramesPerBlock )
3617
/* Make sure, the event isn't already set! */
3618
/* ResetEvent( blockingState->writeBuffersReadyEvent ); */
3620
/* Set the number of requested buffers. */
3621
blockingState->writeBuffersRequested = lFramesPerBlock;
3623
/* Signalize that additional buffers are need. */
3624
blockingState->writeBuffersRequestedFlag = TRUE;
3626
/* Wait until requested number of buffers has been freed. */
3627
waitResult = WaitForSingleObject( blockingState->writeBuffersReadyEvent, timeout );
3629
/* If something seriously went wrong... */
3630
if( waitResult == WAIT_FAILED )
3632
PA_DEBUG(("WaitForSingleObject() failed in WriteStream()\n"));
3633
result = paUnanticipatedHostError;
3634
PA_ASIO_SET_LAST_SYSTEM_ERROR( GetLastError() );
3637
else if( waitResult == WAIT_TIMEOUT )
3639
PA_DEBUG(("WaitForSingleObject() timed out in WriteStream()\n"));
3641
/* If block processing has stopped, abort! */
3642
if( blockingState->stopFlag ) { return result = paStreamIsStopped; }
3644
/* If a timeout is encountered, give up eventually. */
3645
return result = paTimedOut;
3648
/* Now, the ring buffer contains the required amount of free
3649
space to store the provided number of data frames.
3650
(Therefor we don't need to check the return argument of
3651
PaUtil_GetRingBufferWriteRegions(). ;-) )
3654
/* Retrieve pointer(s) to the ring buffer's current write
3655
position(s). If the first buffer segment is too small to
3656
store the requested number of bytes, an additional second
3657
segment is returned. Otherwise, i.e. if the first segment
3658
is large enough, the second segment's pointer will be NULL.
3660
PaUtil_GetRingBufferWriteRegions(pRb ,
3662
&pRingBufferData1st,
3663
&lRingBufferSize1st,
3664
&pRingBufferData2nd,
3665
&lRingBufferSize2nd);
3667
/* Set number of frames to be copied to the ring buffer. */
3668
PaUtil_SetOutputFrameCount( pBp, lRingBufferSize1st );
3669
/* Setup ring buffer access. */
3670
PaUtil_SetInterleavedOutputChannels(pBp , /* Buffer processor. */
3671
0 , /* The first channel's index. */
3672
pRingBufferData1st, /* First ring buffer segment. */
3673
0 ); /* Use all available channels. */
3675
/* If a second ring buffer segment is required. */
3676
if( lRingBufferSize2nd ) {
3677
/* Set number of frames to be copied to the ring buffer. */
3678
PaUtil_Set2ndOutputFrameCount( pBp, lRingBufferSize2nd );
3679
/* Setup ring buffer access. */
3680
PaUtil_Set2ndInterleavedOutputChannels(pBp , /* Buffer processor. */
3681
0 , /* The first channel's index. */
3682
pRingBufferData2nd, /* Second ring buffer segment. */
3683
0 ); /* Use all available channels. */
3686
/* Let the buffer processor handle "copy and conversion" and
3687
update the ring buffer indices manually. */
3688
lFramesCopied = PaUtil_CopyOutput( pBp, &userBuffer, lFramesPerBlock );
3689
PaUtil_AdvanceRingBufferWriteIndex( pRb, lFramesCopied );
3691
/* Decrease number of unprocessed frames. */
3692
lFramesRemaining -= lFramesCopied;
3694
} /* Continue with the next data chunk. */
3695
while( lFramesRemaining );
3698
/* If there has been an output underflow within the callback */
3699
if( blockingState->outputUnderflowFlag )
3701
blockingState->outputUnderflowFlag = FALSE;
3703
/* Return the corresponding error code. */
3704
result = paOutputUnderflowed;
3707
} /* If this is not an output stream. */
3710
result = paCanNotWriteToAnInputOnlyStream;
3717
static signed long GetStreamReadAvailable( PaStream* s )
3719
PaAsioStream *stream = (PaAsioStream*)s;
3721
/* Call buffer utility routine to get the number of available frames. */
3722
return PaUtil_GetRingBufferReadAvailable( &stream->blockingState->readRingBuffer );
3726
static signed long GetStreamWriteAvailable( PaStream* s )
3728
PaAsioStream *stream = (PaAsioStream*)s;
3730
/* Call buffer utility routine to get the number of empty buffers. */
3731
return PaUtil_GetRingBufferWriteAvailable( &stream->blockingState->writeRingBuffer );
3735
/* This routine will be called by the PortAudio engine when audio is needed.
3736
** It may called at interrupt level on some machines so don't do anything
3737
** that could mess up the system like calling malloc() or free().
3739
static int BlockingIoPaCallback(const void *inputBuffer ,
3740
void *outputBuffer ,
3741
unsigned long framesPerBuffer,
3742
const PaStreamCallbackTimeInfo *timeInfo ,
3743
PaStreamCallbackFlags statusFlags ,
3746
PaError result = paNoError; /* Initial return value. */
3747
PaAsioStream *stream = *(PaAsioStream**)userData; /* The PA ASIO stream. */
3748
PaAsioStreamBlockingState *blockingState = stream->blockingState; /* Persume blockingState is valid, otherwise the callback wouldn't be running. */
3750
/* Get a pointer to the stream's blocking i/o buffer processor. */
3751
PaUtilBufferProcessor *pBp = &blockingState->bufferProcessor;
3752
PaUtilRingBuffer *pRb = NULL;
3754
/* If output data has been requested. */
3755
if( stream->outputChannelCount )
3757
/* If the callback input argument signalizes a output underflow,
3758
make sure the WriteStream() function knows about it, too! */
3759
if( statusFlags & paOutputUnderflowed ) {
3760
blockingState->outputUnderflowFlag = TRUE;
3763
/* Access the corresponding ring buffer. */
3764
pRb = &blockingState->writeRingBuffer;
3766
/* If the blocking i/o buffer contains enough output data, */
3767
if( PaUtil_GetRingBufferReadAvailable(pRb) >= (long) framesPerBuffer )
3769
/* Extract the requested data from the ring buffer. */
3770
PaUtil_ReadRingBuffer( pRb, outputBuffer, framesPerBuffer );
3772
else /* If no output data is available :-( */
3774
/* Signalize a write-buffer underflow. */
3775
blockingState->outputUnderflowFlag = TRUE;
3777
/* Fill the output buffer with silence. */
3778
(*pBp->outputZeroer)( outputBuffer, 1, pBp->outputChannelCount * framesPerBuffer );
3780
/* If playback is to be stopped */
3781
if( blockingState->stopFlag && PaUtil_GetRingBufferReadAvailable(pRb) < (long) framesPerBuffer )
3783
/* Extract all the remaining data from the ring buffer,
3784
whether it is a complete data block or not. */
3785
PaUtil_ReadRingBuffer( pRb, outputBuffer, PaUtil_GetRingBufferReadAvailable(pRb) );
3789
/* Set blocking i/o event? */
3790
if( blockingState->writeBuffersRequestedFlag && PaUtil_GetRingBufferWriteAvailable(pRb) >= (long) blockingState->writeBuffersRequested )
3792
/* Reset buffer request. */
3793
blockingState->writeBuffersRequestedFlag = FALSE;
3794
blockingState->writeBuffersRequested = 0;
3795
/* Signalize that requested buffers are ready. */
3796
SetEvent( blockingState->writeBuffersReadyEvent );
3797
/* What do we do if SetEvent() returns zero, i.e. the event
3798
could not be set? How to return errors from within the
3799
callback? - S.Fischer */
3803
/* If input data has been supplied. */
3804
if( stream->inputChannelCount )
3806
/* If the callback input argument signalizes a input overflow,
3807
make sure the ReadStream() function knows about it, too! */
3808
if( statusFlags & paInputOverflowed ) {
3809
blockingState->inputOverflowFlag = TRUE;
3812
/* Access the corresponding ring buffer. */
3813
pRb = &blockingState->readRingBuffer;
3815
/* If the blocking i/o buffer contains not enough input buffers */
3816
if( PaUtil_GetRingBufferWriteAvailable(pRb) < (long) framesPerBuffer )
3818
/* Signalize a read-buffer overflow. */
3819
blockingState->inputOverflowFlag = TRUE;
3821
/* Remove some old data frames from the buffer. */
3822
PaUtil_AdvanceRingBufferReadIndex( pRb, framesPerBuffer );
3825
/* Insert the current input data into the ring buffer. */
3826
PaUtil_WriteRingBuffer( pRb, inputBuffer, framesPerBuffer );
3828
/* Set blocking i/o event? */
3829
if( blockingState->readFramesRequestedFlag && PaUtil_GetRingBufferReadAvailable(pRb) >= (long) blockingState->readFramesRequested )
3831
/* Reset buffer request. */
3832
blockingState->readFramesRequestedFlag = FALSE;
3833
blockingState->readFramesRequested = 0;
3834
/* Signalize that requested buffers are ready. */
3835
SetEvent( blockingState->readFramesReadyEvent );
3836
/* What do we do if SetEvent() returns zero, i.e. the event
3837
could not be set? How to return errors from within the
3838
callback? - S.Fischer */
3839
/** @todo report an error with PA_DEBUG */
3847
PaError PaAsio_ShowControlPanel( PaDeviceIndex device, void* systemSpecific )
3849
PaError result = paNoError;
3850
PaUtilHostApiRepresentation *hostApi;
3851
PaDeviceIndex hostApiDevice;
3852
ASIODriverInfo asioDriverInfo;
3853
ASIOError asioError;
3854
int asioIsInitialized = 0;
3855
PaAsioHostApiRepresentation *asioHostApi;
3856
PaAsioDeviceInfo *asioDeviceInfo;
3859
result = PaUtil_GetHostApiRepresentation( &hostApi, paASIO );
3860
if( result != paNoError )
3863
result = PaUtil_DeviceIndexToHostApiDeviceIndex( &hostApiDevice, device, hostApi );
3864
if( result != paNoError )
3868
In theory we could proceed if the currently open device was the same
3869
one for which the control panel was requested, however because the
3870
window pointer is not available until this function is called we
3871
currently need to call ASIOInit() again here, which of course can't be
3872
done safely while a stream is open.
3875
asioHostApi = (PaAsioHostApiRepresentation*)hostApi;
3876
if( asioHostApi->openAsioDeviceIndex != paNoDevice )
3878
result = paDeviceUnavailable;
3882
asioDeviceInfo = (PaAsioDeviceInfo*)hostApi->deviceInfos[hostApiDevice];
3884
/* See notes about CoInitialize(0) in LoadAsioDriver(). */
3887
if( !asioHostApi->asioDrivers->loadDriver( const_cast<char*>(asioDeviceInfo->commonDeviceInfo.name) ) )
3889
result = paUnanticipatedHostError;
3894
memset( &asioDriverInfo, 0, sizeof(ASIODriverInfo) );
3895
asioDriverInfo.asioVersion = 2;
3896
asioDriverInfo.sysRef = systemSpecific;
3897
asioError = ASIOInit( &asioDriverInfo );
3898
if( asioError != ASE_OK )
3900
result = paUnanticipatedHostError;
3901
PA_ASIO_SET_LAST_ASIO_ERROR( asioError );
3906
asioIsInitialized = 1;
3909
PA_DEBUG(("PaAsio_ShowControlPanel: ASIOInit(): %s\n", PaAsio_GetAsioErrorText(asioError) ));
3910
PA_DEBUG(("asioVersion: ASIOInit(): %ld\n", asioDriverInfo.asioVersion ));
3911
PA_DEBUG(("driverVersion: ASIOInit(): %ld\n", asioDriverInfo.driverVersion ));
3912
PA_DEBUG(("Name: ASIOInit(): %s\n", asioDriverInfo.name ));
3913
PA_DEBUG(("ErrorMessage: ASIOInit(): %s\n", asioDriverInfo.errorMessage ));
3915
asioError = ASIOControlPanel();
3916
if( asioError != ASE_OK )
3918
PA_DEBUG(("PaAsio_ShowControlPanel: ASIOControlPanel(): %s\n", PaAsio_GetAsioErrorText(asioError) ));
3919
result = paUnanticipatedHostError;
3920
PA_ASIO_SET_LAST_ASIO_ERROR( asioError );
3924
PA_DEBUG(("PaAsio_ShowControlPanel: ASIOControlPanel(): %s\n", PaAsio_GetAsioErrorText(asioError) ));
3926
asioError = ASIOExit();
3927
if( asioError != ASE_OK )
3929
result = paUnanticipatedHostError;
3930
PA_ASIO_SET_LAST_ASIO_ERROR( asioError );
3931
asioIsInitialized = 0;
3936
PA_DEBUG(("PaAsio_ShowControlPanel: ASIOExit(): %s\n", PaAsio_GetAsioErrorText(asioError) ));
3941
if( asioIsInitialized )
3951
PaError PaAsio_GetInputChannelName( PaDeviceIndex device, int channelIndex,
3952
const char** channelName )
3954
PaError result = paNoError;
3955
PaUtilHostApiRepresentation *hostApi;
3956
PaDeviceIndex hostApiDevice;
3957
PaAsioDeviceInfo *asioDeviceInfo;
3960
result = PaUtil_GetHostApiRepresentation( &hostApi, paASIO );
3961
if( result != paNoError )
3964
result = PaUtil_DeviceIndexToHostApiDeviceIndex( &hostApiDevice, device, hostApi );
3965
if( result != paNoError )
3968
asioDeviceInfo = (PaAsioDeviceInfo*)hostApi->deviceInfos[hostApiDevice];
3970
if( channelIndex < 0 || channelIndex >= asioDeviceInfo->commonDeviceInfo.maxInputChannels ){
3971
result = paInvalidChannelCount;
3975
*channelName = asioDeviceInfo->asioChannelInfos[channelIndex].name;
3984
PaError PaAsio_GetOutputChannelName( PaDeviceIndex device, int channelIndex,
3985
const char** channelName )
3987
PaError result = paNoError;
3988
PaUtilHostApiRepresentation *hostApi;
3989
PaDeviceIndex hostApiDevice;
3990
PaAsioDeviceInfo *asioDeviceInfo;
3993
result = PaUtil_GetHostApiRepresentation( &hostApi, paASIO );
3994
if( result != paNoError )
3997
result = PaUtil_DeviceIndexToHostApiDeviceIndex( &hostApiDevice, device, hostApi );
3998
if( result != paNoError )
4001
asioDeviceInfo = (PaAsioDeviceInfo*)hostApi->deviceInfos[hostApiDevice];
4003
if( channelIndex < 0 || channelIndex >= asioDeviceInfo->commonDeviceInfo.maxOutputChannels ){
4004
result = paInvalidChannelCount;
4008
*channelName = asioDeviceInfo->asioChannelInfos[
4009
asioDeviceInfo->commonDeviceInfo.maxInputChannels + channelIndex].name;
4018
/* NOTE: the following functions are ASIO-stream specific, and are called directly
4019
by client code. We need to check for many more error conditions here because
4020
we don't have the benefit of pa_front.c's parameter checking.
4023
static PaError GetAsioStreamPointer( PaAsioStream **stream, PaStream *s )
4026
PaUtilHostApiRepresentation *hostApi;
4027
PaAsioHostApiRepresentation *asioHostApi;
4029
result = PaUtil_ValidateStreamPointer( s );
4030
if( result != paNoError )
4033
result = PaUtil_GetHostApiRepresentation( &hostApi, paASIO );
4034
if( result != paNoError )
4037
asioHostApi = (PaAsioHostApiRepresentation*)hostApi;
4039
if( PA_STREAM_REP( s )->streamInterface == &asioHostApi->callbackStreamInterface
4040
|| PA_STREAM_REP( s )->streamInterface == &asioHostApi->blockingStreamInterface )
4042
/* s is an ASIO stream */
4043
*stream = (PaAsioStream *)s;
4048
return paIncompatibleStreamHostApi;
4053
PaError PaAsio_SetStreamSampleRate( PaStream* s, double sampleRate )
4055
PaAsioStream *stream;
4056
PaError result = GetAsioStreamPointer( &stream, s );
4057
if( result != paNoError )
4060
if( stream != theAsioStream )
4061
return paBadStreamPtr;
4063
return ValidateAndSetSampleRate( sampleRate );