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

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

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

root / src / portaudio_20161030_catalina_patch / src / hostapi / asihpi / pa_linux_asihpi.c @ 164:9fa11135915a

History | View | Annotate | Download (115 KB)

1
/*
2
 * $Id:$
3
 * PortAudio Portable Real-Time Audio Library
4
 * Latest Version at: http://www.portaudio.com
5
 * AudioScience HPI implementation by Fred Gleason, Ludwig Schwardt and
6
 * Eliot Blennerhassett
7
 *
8
 * Copyright (c) 2003 Fred Gleason <fredg@salemradiolabs.com>
9
 * Copyright (c) 2005,2006 Ludwig Schwardt <schwardt@sun.ac.za>
10
 * Copyright (c) 2011 Eliot Blennerhassett <eblennerhassett@audioscience.com>
11
 *
12
 * Based on the Open Source API proposed by Ross Bencina
13
 * Copyright (c) 1999-2008 Ross Bencina, Phil Burk
14
 *
15
 * Permission is hereby granted, free of charge, to any person obtaining
16
 * a copy of this software and associated documentation files
17
 * (the "Software"), to deal in the Software without restriction,
18
 * including without limitation the rights to use, copy, modify, merge,
19
 * publish, distribute, sublicense, and/or sell copies of the Software,
20
 * and to permit persons to whom the Software is furnished to do so,
21
 * subject to the following conditions:
22
 *
23
 * The above copyright notice and this permission notice shall be
24
 * included in all copies or substantial portions of the Software.
25
 *
26
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
27
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
28
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
29
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
30
 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
31
 * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
32
 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
33
 */
34

    
35
/*
36
 * The text above constitutes the entire PortAudio license; however,
37
 * the PortAudio community also makes the following non-binding requests:
38
 *
39
 * Any person wishing to distribute modifications to the Software is
40
 * requested to send the modifications to the original developer so that
41
 * they can be incorporated into the canonical version. It is also
42
 * requested that these non-binding requests be included along with the
43
 * license above.
44
 */
45

    
46
/*
47
 * Modification History
48
 * 12/2003 - Initial version
49
 * 09/2005 - v19 version [rewrite]
50
 */
51

    
52
/** @file
53
 @ingroup hostapi_src
54
 @brief Host API implementation supporting AudioScience cards
55
        via the Linux HPI interface.
56

57
 <h3>Overview</h3>
58

59
 This is a PortAudio implementation for the AudioScience HPI Audio API
60
 on the Linux platform. AudioScience makes a range of audio adapters customised
61
 for the broadcasting industry, with support for both Windows and Linux.
62
 More information on their products can be found on their website:
63

64
     http://www.audioscience.com
65

66
 Documentation for the HPI API can be found at:
67

68
     http://www.audioscience.com/internet/download/sdk/hpi_usermanual_html/html/index.html
69

70
 The Linux HPI driver itself (a kernel module + library) can be downloaded from:
71

72
     http://www.audioscience.com/internet/download/linux_drivers.htm
73

74
 <h3>Implementation strategy</h3>
75

76
 *Note* Ideally, AudioScience cards should be handled by the PortAudio ALSA
77
 implementation on Linux, as ALSA is the preferred Linux soundcard API. The existence
78
 of this host API implementation might therefore seem a bit flawed. Unfortunately, at
79
 the time of the creation of this implementation (June 2006), the PA ALSA implementation
80
 could not make use of the existing AudioScience ALSA driver. PA ALSA uses the
81
 "memory-mapped" (mmap) ALSA access mode to interact with the ALSA library, while the
82
 AudioScience ALSA driver only supports the "read-write" access mode. The appropriate
83
 solution to this problem is to add "read-write" support to PortAudio ALSA, thereby
84
 extending the range of soundcards it supports (AudioScience cards are not the only
85
 ones with this problem). Given the author's limited knowledge of ALSA and the
86
 simplicity of the HPI API, the second-best solution was born...
87

88
 The following mapping between HPI and PA was followed:
89
 HPI subsystem => PortAudio host API
90
 HPI adapter => nothing specific
91
 HPI stream => PortAudio device
92

93
 Each HPI stream is either input or output (not both), and can support
94
 different channel counts, sampling rates and sample formats. It is therefore
95
 a more natural fit to a PA device. A PA stream can therefore combine two
96
 HPI streams (one input and one output) into a "full-duplex" stream. These
97
 HPI streams can even be on different physical adapters. The two streams ought to be
98
 sample-synchronised when they reside on the same adapter, as most AudioScience adapters
99
 derive their ADC and DAC clocks from one master clock. When combining two adapters
100
 into one full-duplex stream, however, the use of a word clock connection between the
101
 adapters is strongly recommended.
102

103
 The HPI interface is inherently blocking, making use of read and write calls to
104
 transfer data between user buffers and driver buffers. The callback interface therefore
105
 requires a helper thread ("callback engine") which periodically transfers data (one thread
106
 per PA stream, in fact). The current implementation explicitly sleeps via Pa_Sleep() until
107
 enough samples can be transferred (select() or poll() would be better, but currently seems
108
 impossible...). The thread implementation makes use of the Unix thread helper functions
109
 and some pthread calls here and there. If a unified PA thread exists, this host API
110
 implementation might also compile on Windows, as this is the only real Linux-specific
111
 part of the code.
112

113
 There is no inherent fixed buffer size in the HPI interface, as in some other host APIs.
114
 The PortAudio implementation contains a buffer that is allocated during OpenStream and
115
 used to transfer data between the callback and the HPI driver buffer. The size of this
116
 buffer is quite flexible and is derived from latency suggestions and matched to the
117
 requested callback buffer size as far as possible. It can become quite huge, as the
118
 AudioScience cards are typically geared towards higher-latency applications and contain
119
 large hardware buffers.
120

121
 The HPI interface natively supports most common sample formats and sample rates (some
122
 conversion is done on the adapter itself).
123

124
 Stream time is measured based on the number of processed frames, which is adjusted by the
125
 number of frames currently buffered by the HPI driver.
126

127
 There is basic support for detecting overflow and underflow. The HPI interface does not
128
 explicitly indicate this, so thresholds on buffer levels are used in combination with
129
 stream state. Recovery from overflow and underflow is left to the PA client.
130

131
 Blocking streams are also implemented. It makes use of the same polling routines that
132
 the callback interface uses, in order to prevent the allocation of variable-sized
133
 buffers during reading and writing. The framesPerBuffer parameter is therefore still
134
 relevant, and this can be increased in the blocking case to improve efficiency.
135

136
 The implementation contains extensive reporting macros (slightly modified PA_ENSURE and
137
 PA_UNLESS versions) and a useful stream dump routine to provide debugging feedback.
138

139
 Output buffer priming via the user callback (i.e. paPrimeOutputBuffersUsingStreamCallback
140
 and friends) is not implemented yet. All output is primed with silence.
141
 */
142

    
143
#include <unistd.h>
144
#include <stdio.h>
145
#include <stdlib.h>
146
#include <string.h>          /* strlen() */
147
#include <pthread.h>         /* pthreads and friends */
148
#include <assert.h>          /* assert */
149
#include <math.h>            /* ceil, floor */
150

    
151
#include <asihpi/hpi.h>      /* HPI API */
152

    
153
#include "portaudio.h"       /* PortAudio API */
154
#include "pa_util.h"         /* PA_DEBUG, other small utilities */
155
#include "pa_unix_util.h"    /* Unix threading utilities */
156
#include "pa_allocation.h"   /* Group memory allocation */
157
#include "pa_hostapi.h"      /* Host API structs */
158
#include "pa_stream.h"       /* Stream interface structs */
159
#include "pa_cpuload.h"      /* CPU load measurer */
160
#include "pa_process.h"      /* Buffer processor */
161
#include "pa_converters.h"   /* PaUtilZeroer */
162
#include "pa_debugprint.h"
163

    
164
/* -------------------------------------------------------------------------- */
165

    
166
/*
167
 * Defines
168
 */
169

    
170
/* Error reporting and assertions */
171

    
172
/** Evaluate expression, and return on any PortAudio errors */
173
#define PA_ENSURE_(expr) \
174
    do { \
175
        PaError paError = (expr); \
176
        if( UNLIKELY( paError < paNoError ) ) \
177
        { \
178
            PA_DEBUG(( "Expression '" #expr "' failed in '" __FILE__ "', line: " STRINGIZE( __LINE__ ) "\n" )); \
179
            result = paError; \
180
            goto error; \
181
        } \
182
    } while (0);
183

    
184
/** Assert expression, else return the provided PaError */
185
#define PA_UNLESS_(expr, paError) \
186
    do { \
187
        if( UNLIKELY( (expr) == 0 ) ) \
188
        { \
189
            PA_DEBUG(( "Expression '" #expr "' failed in '" __FILE__ "', line: " STRINGIZE( __LINE__ ) "\n" )); \
190
            result = (paError); \
191
            goto error; \
192
        } \
193
    } while( 0 );
194

    
195
/** Check return value of HPI function, and map it to PaError */
196
#define PA_ASIHPI_UNLESS_(expr, paError) \
197
    do { \
198
        hpi_err_t hpiError = (expr); \
199
        /* If HPI error occurred */ \
200
        if( UNLIKELY( hpiError ) ) \
201
        { \
202
            char szError[256]; \
203
            HPI_GetErrorText( hpiError, szError ); \
204
            PA_DEBUG(( "HPI error %d occurred: %s\n", hpiError, szError )); \
205
            /* This message will always be displayed, even if debug info is disabled */ \
206
            PA_DEBUG(( "Expression '" #expr "' failed in '" __FILE__ "', line: " STRINGIZE( __LINE__ ) "\n" )); \
207
            if( (paError) == paUnanticipatedHostError ) \
208
            { \
209
                PA_DEBUG(( "Host error description: %s\n", szError )); \
210
                /* PaUtil_SetLastHostErrorInfo should only be used in the main thread */ \
211
                if( pthread_equal( pthread_self(), paUnixMainThread ) ) \
212
                { \
213
                    PaUtil_SetLastHostErrorInfo( paInDevelopment, hpiError, szError ); \
214
                } \
215
            } \
216
            /* If paNoError is specified, continue as usual */ \
217
            /* (useful if you only want to print out the debug messages above) */ \
218
            if( (paError) < 0 ) \
219
            { \
220
                result = (paError); \
221
                goto error; \
222
            } \
223
        } \
224
    } while( 0 );
225

    
226
/** Report HPI error code and text */
227
#define PA_ASIHPI_REPORT_ERROR_(hpiErrorCode) \
228
    do { \
229
        char szError[256]; \
230
        HPI_GetErrorText( hpiError, szError ); \
231
        PA_DEBUG(( "HPI error %d occurred: %s\n", hpiError, szError )); \
232
        /* PaUtil_SetLastHostErrorInfo should only be used in the main thread */ \
233
        if( pthread_equal( pthread_self(), paUnixMainThread ) ) \
234
        { \
235
            PaUtil_SetLastHostErrorInfo( paInDevelopment, (hpiErrorCode), szError ); \
236
        } \
237
    } while( 0 );
238

    
239
/* Defaults */
240

    
241
/** Sample formats available natively on AudioScience hardware */
242
#define PA_ASIHPI_AVAILABLE_FORMATS_ (paFloat32 | paInt32 | paInt24 | paInt16 | paUInt8)
243
/** Enable background bus mastering (BBM) for buffer transfers, if available (see HPI docs) */
244
#define PA_ASIHPI_USE_BBM_ 1
245
/** Minimum number of frames in HPI buffer (for either data or available space).
246
 If buffer contains less data/space, it indicates xrun or completion. */
247
#define PA_ASIHPI_MIN_FRAMES_ 1152
248
/** Minimum polling interval in milliseconds, which determines minimum host buffer size */
249
#define PA_ASIHPI_MIN_POLLING_INTERVAL_ 10
250

    
251
/* -------------------------------------------------------------------------- */
252

    
253
/*
254
 * Structures
255
 */
256

    
257
/** Host API global data */
258
typedef struct PaAsiHpiHostApiRepresentation
259
{
260
    /* PortAudio "base class" - keep the baseRep first! (C-style inheritance) */
261
    PaUtilHostApiRepresentation baseHostApiRep;
262
    PaUtilStreamInterface callbackStreamInterface;
263
    PaUtilStreamInterface blockingStreamInterface;
264

    
265
    PaUtilAllocationGroup *allocations;
266

    
267
    /* implementation specific data goes here */
268

    
269
    PaHostApiIndex hostApiIndex;
270
}
271
PaAsiHpiHostApiRepresentation;
272

    
273

    
274
/** Device data */
275
typedef struct PaAsiHpiDeviceInfo
276
{
277
    /* PortAudio "base class" - keep the baseRep first! (C-style inheritance) */
278
    /** Common PortAudio device information */
279
    PaDeviceInfo baseDeviceInfo;
280

    
281
    /* implementation specific data goes here */
282

    
283
    /** Adapter index */
284
    uint16_t adapterIndex;
285
    /** Adapter model number (hex) */
286
    uint16_t adapterType;
287
    /** Adapter HW/SW version */
288
    uint16_t adapterVersion;
289
    /** Adapter serial number */
290
    uint32_t adapterSerialNumber;
291
    /** Stream number */
292
    uint16_t streamIndex;
293
    /** 0=Input, 1=Output (HPI streams are either input or output but not both) */
294
    uint16_t streamIsOutput;
295
}
296
PaAsiHpiDeviceInfo;
297

    
298

    
299
/** Stream state as defined by PortAudio.
300
 It seems that the host API implementation has to keep track of the PortAudio stream state.
301
 Please note that this is NOT the same as the state of the underlying HPI stream. By separating
302
 these two concepts, a lot of flexibility is gained. There is a rough match between the two,
303
 of course, but forcing a precise match is difficult. For example, HPI_STATE_DRAINED can occur
304
 during the Active state of PortAudio (due to underruns) and also during CallBackFinished in
305
 the case of an output stream. Similarly, HPI_STATE_STOPPED mostly coincides with the Stopped
306
 PortAudio state, by may also occur in the CallbackFinished state when recording is finished.
307

308
 Here is a rough match-up:
309

310
 PortAudio state   =>     HPI state
311
 ---------------          ---------
312
 Active            =>     HPI_STATE_RECORDING, HPI_STATE_PLAYING, (HPI_STATE_DRAINED)
313
 Stopped           =>     HPI_STATE_STOPPED
314
 CallbackFinished  =>     HPI_STATE_STOPPED, HPI_STATE_DRAINED */
315
typedef enum PaAsiHpiStreamState
316
{
317
    paAsiHpiStoppedState=0,
318
    paAsiHpiActiveState=1,
319
    paAsiHpiCallbackFinishedState=2
320
}
321
PaAsiHpiStreamState;
322

    
323

    
324
/** Stream component data (associated with one direction, i.e. either input or output) */
325
typedef struct PaAsiHpiStreamComponent
326
{
327
    /** Device information (HPI handles, etc) */
328
    PaAsiHpiDeviceInfo *hpiDevice;
329
    /** Stream handle, as passed to HPI interface. */
330
    hpi_handle_t hpiStream;
331
    /** Stream format, as passed to HPI interface */
332
    struct hpi_format hpiFormat;
333
    /** Number of bytes per frame, derived from hpiFormat and saved for convenience */
334
    uint32_t bytesPerFrame;
335
    /** Size of hardware (on-card) buffer of stream in bytes */
336
    uint32_t hardwareBufferSize;
337
    /** Size of host (BBM) buffer of stream in bytes (if used) */
338
    uint32_t hostBufferSize;
339
    /** Upper limit on the utilization of output stream buffer (both hardware and host).
340
     This prevents large latencies in an output-only stream with a potentially huge buffer
341
     and a fast data generator, which would otherwise keep the hardware buffer filled to
342
     capacity. See also the "Hardware Buffering=off" option in the AudioScience WAV driver. */
343
    uint32_t outputBufferCap;
344
    /** Sample buffer (halfway station between HPI and buffer processor) */
345
    uint8_t *tempBuffer;
346
    /** Sample buffer size, in bytes */
347
    uint32_t tempBufferSize;
348
}
349
PaAsiHpiStreamComponent;
350

    
351

    
352
/** Stream data */
353
typedef struct PaAsiHpiStream
354
{
355
    /* PortAudio "base class" - keep the baseRep first! (C-style inheritance) */
356
    PaUtilStreamRepresentation baseStreamRep;
357
    PaUtilCpuLoadMeasurer cpuLoadMeasurer;
358
    PaUtilBufferProcessor bufferProcessor;
359

    
360
    PaUtilAllocationGroup *allocations;
361

    
362
    /* implementation specific data goes here */
363

    
364
    /** Separate structs for input and output sides of stream */
365
    PaAsiHpiStreamComponent *input, *output;
366

    
367
    /** Polling interval (in milliseconds) */
368
    uint32_t pollingInterval;
369
    /** Are we running in callback mode? */
370
    int callbackMode;
371
    /** Number of frames to transfer at a time to/from HPI */
372
    unsigned long maxFramesPerHostBuffer;
373
    /** Indicates that the stream is in the paNeverDropInput mode */
374
    int neverDropInput;
375
    /** Contains copy of user buffers, used by blocking interface to transfer non-interleaved data.
376
     It went here instead of to each stream component, as the stream component buffer setup in
377
     PaAsiHpi_SetupBuffers doesn't know the stream details such as callbackMode.
378
     (Maybe a problem later if ReadStream and WriteStream happens concurrently on same stream.) */
379
    void **blockingUserBufferCopy;
380

    
381
    /* Thread-related variables */
382

    
383
    /** Helper thread which will deliver data to user callback */
384
    PaUnixThread thread;
385
    /** PortAudio stream state (Active/Stopped/CallbackFinished) */
386
    volatile sig_atomic_t state;
387
    /** Hard abort, i.e. drop frames? */
388
    volatile sig_atomic_t callbackAbort;
389
    /** True if stream stopped via exiting callback with paComplete/paAbort flag
390
     (as opposed to explicit call to StopStream/AbortStream) */
391
    volatile sig_atomic_t callbackFinished;
392
}
393
PaAsiHpiStream;
394

    
395

    
396
/** Stream state information, collected together for convenience */
397
typedef struct PaAsiHpiStreamInfo
398
{
399
    /** HPI stream state (HPI_STATE_STOPPED, HPI_STATE_PLAYING, etc.) */
400
    uint16_t state;
401
    /** Size (in bytes) of recording/playback data buffer in HPI driver */
402
    uint32_t bufferSize;
403
    /** Amount of data (in bytes) available in the buffer */
404
    uint32_t dataSize;
405
    /** Number of frames played/recorded since last stream reset */
406
    uint32_t frameCounter;
407
    /** Amount of data (in bytes) in hardware (on-card) buffer.
408
     This differs from dataSize if bus mastering (BBM) is used, which introduces another
409
     driver-level buffer to which dataSize/bufferSize then refers. */
410
    uint32_t auxDataSize;
411
    /** Total number of data frames currently buffered by HPI driver (host + hw buffers) */
412
    uint32_t totalBufferedData;
413
    /** Size of immediately available data (for input) or space (for output) in frames.
414
     This only checks the first-level buffer (typically host buffer). This amount can be
415
     transferred immediately. */
416
    uint32_t availableFrames;
417
    /** Indicates that hardware buffer is getting too full */
418
    int overflow;
419
    /** Indicates that hardware buffer is getting too empty */
420
    int underflow;
421
}
422
PaAsiHpiStreamInfo;
423

    
424
/* -------------------------------------------------------------------------- */
425

    
426
/*
427
 * Function prototypes
428
 */
429

    
430
#ifdef __cplusplus
431
extern "C"
432
{
433
#endif /* __cplusplus */
434

    
435
    /* The only exposed function in the entire host API implementation */
436
    PaError PaAsiHpi_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
437

    
438
#ifdef __cplusplus
439
}
440
#endif /* __cplusplus */
441

    
442
static void Terminate( struct PaUtilHostApiRepresentation *hostApi );
443
static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
444
                                  const PaStreamParameters *inputParameters,
445
                                  const PaStreamParameters *outputParameters,
446
                                  double sampleRate );
447

    
448
/* Stream prototypes */
449
static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
450
                           PaStream **s,
451
                           const PaStreamParameters *inputParameters,
452
                           const PaStreamParameters *outputParameters,
453
                           double sampleRate,
454
                           unsigned long framesPerBuffer,
455
                           PaStreamFlags streamFlags,
456
                           PaStreamCallback *streamCallback,
457
                           void *userData );
458
static PaError CloseStream( PaStream *s );
459
static PaError StartStream( PaStream *s );
460
static PaError StopStream( PaStream *s );
461
static PaError AbortStream( PaStream *s );
462
static PaError IsStreamStopped( PaStream *s );
463
static PaError IsStreamActive( PaStream *s );
464
static PaTime GetStreamTime( PaStream *s );
465
static double GetStreamCpuLoad( PaStream *s );
466

    
467
/* Blocking prototypes */
468
static PaError ReadStream( PaStream *s, void *buffer, unsigned long frames );
469
static PaError WriteStream( PaStream *s, const void *buffer, unsigned long frames );
470
static signed long GetStreamReadAvailable( PaStream *s );
471
static signed long GetStreamWriteAvailable( PaStream *s );
472

    
473
/* Callback prototypes */
474
static void *CallbackThreadFunc( void *userData );
475

    
476
/* Functions specific to this API */
477
static PaError PaAsiHpi_BuildDeviceList( PaAsiHpiHostApiRepresentation *hpiHostApi );
478
static uint16_t PaAsiHpi_PaToHpiFormat( PaSampleFormat paFormat );
479
static PaSampleFormat PaAsiHpi_HpiToPaFormat( uint16_t hpiFormat );
480
static PaError PaAsiHpi_CreateFormat( struct PaUtilHostApiRepresentation *hostApi,
481
                                      const PaStreamParameters *parameters, double sampleRate,
482
                                      PaAsiHpiDeviceInfo **hpiDevice, struct hpi_format *hpiFormat );
483
static PaError PaAsiHpi_OpenInput( struct PaUtilHostApiRepresentation *hostApi,
484
                                   const PaAsiHpiDeviceInfo *hpiDevice, const struct hpi_format *hpiFormat,
485
                                   hpi_handle_t *hpiStream );
486
static PaError PaAsiHpi_OpenOutput( struct PaUtilHostApiRepresentation *hostApi,
487
                                    const PaAsiHpiDeviceInfo *hpiDevice, const struct hpi_format *hpiFormat,
488
                                    hpi_handle_t *hpiStream );
489
static PaError PaAsiHpi_GetStreamInfo( PaAsiHpiStreamComponent *streamComp, PaAsiHpiStreamInfo *info );
490
static void PaAsiHpi_StreamComponentDump( PaAsiHpiStreamComponent *streamComp, PaAsiHpiStream *stream );
491
static void PaAsiHpi_StreamDump( PaAsiHpiStream *stream );
492
static PaError PaAsiHpi_SetupBuffers( PaAsiHpiStreamComponent *streamComp, uint32_t pollingInterval,
493
                                      unsigned long framesPerPaHostBuffer, PaTime suggestedLatency );
494
static PaError PaAsiHpi_PrimeOutputWithSilence( PaAsiHpiStream *stream );
495
static PaError PaAsiHpi_StartStream( PaAsiHpiStream *stream, int outputPrimed );
496
static PaError PaAsiHpi_StopStream( PaAsiHpiStream *stream, int abort );
497
static PaError PaAsiHpi_ExplicitStop( PaAsiHpiStream *stream, int abort );
498
static void PaAsiHpi_OnThreadExit( void *userData );
499
static PaError PaAsiHpi_WaitForFrames( PaAsiHpiStream *stream, unsigned long *framesAvail,
500
                                       PaStreamCallbackFlags *cbFlags );
501
static void PaAsiHpi_CalculateTimeInfo( PaAsiHpiStream *stream, PaStreamCallbackTimeInfo *timeInfo );
502
static PaError PaAsiHpi_BeginProcessing( PaAsiHpiStream* stream, unsigned long* numFrames,
503
        PaStreamCallbackFlags *cbFlags );
504
static PaError PaAsiHpi_EndProcessing( PaAsiHpiStream *stream, unsigned long numFrames,
505
                                       PaStreamCallbackFlags *cbFlags );
506

    
507
/* ==========================================================================
508
 * ============================= IMPLEMENTATION =============================
509
 * ========================================================================== */
510

    
511
/* --------------------------- Host API Interface --------------------------- */
512

    
513
/** Enumerate all PA devices (= HPI streams).
514
 This compiles a list of all HPI adapters, and registers a PA device for each input and
515
 output stream it finds. Most errors are ignored, as missing or erroneous devices are
516
 simply skipped.
517

518
 @param hpiHostApi Pointer to HPI host API struct
519

520
 @return PortAudio error code (only paInsufficientMemory in practice)
521
 */
522
static PaError PaAsiHpi_BuildDeviceList( PaAsiHpiHostApiRepresentation *hpiHostApi )
523
{
524
    PaError result = paNoError;
525
    PaUtilHostApiRepresentation *hostApi = &hpiHostApi->baseHostApiRep;
526
    PaHostApiInfo *baseApiInfo = &hostApi->info;
527
    PaAsiHpiDeviceInfo *hpiDeviceList;
528
    int numAdapters;
529
    hpi_err_t hpiError = 0;
530
    int i, j, deviceCount = 0, deviceIndex = 0;
531

    
532
    assert( hpiHostApi );
533

    
534
    /* Errors not considered critical here (subsystem may report 0 devices), but report them */
535
    /* in debug mode. */
536
    PA_ASIHPI_UNLESS_( HPI_SubSysGetNumAdapters( NULL, &numAdapters), paNoError );
537

    
538
    for( i=0; i < numAdapters; ++i )
539
    {
540
        uint16_t inStreams, outStreams;
541
        uint16_t version;
542
        uint32_t serial;
543
        uint16_t type;
544
        uint32_t idx;
545

    
546
        hpiError = HPI_SubSysGetAdapter(NULL, i, &idx, &type);
547
        if (hpiError)
548
            continue;
549

    
550
        /* Try to open adapter */
551
        hpiError = HPI_AdapterOpen( NULL, idx );
552
        /* Report error and skip to next device on failure */
553
        if( hpiError )
554
        {
555
            PA_ASIHPI_REPORT_ERROR_( hpiError );
556
            continue;
557
        }
558
        hpiError = HPI_AdapterGetInfo( NULL, idx, &outStreams, &inStreams,
559
                                        &version, &serial, &type );
560
        /* Skip to next device on failure */
561
        if( hpiError )
562
        {
563
            PA_ASIHPI_REPORT_ERROR_( hpiError );
564
            continue;
565
        }
566
        else
567
        {
568
            /* Assign default devices if available and increment device count */
569
            if( (baseApiInfo->defaultInputDevice == paNoDevice) && (inStreams > 0) )
570
                baseApiInfo->defaultInputDevice = deviceCount;
571
            deviceCount += inStreams;
572
            if( (baseApiInfo->defaultOutputDevice == paNoDevice) && (outStreams > 0) )
573
                baseApiInfo->defaultOutputDevice = deviceCount;
574
            deviceCount += outStreams;
575
        }
576
    }
577

    
578
    /* Register any discovered devices */
579
    if( deviceCount > 0 )
580
    {
581
        /* Memory allocation */
582
        PA_UNLESS_( hostApi->deviceInfos = (PaDeviceInfo**) PaUtil_GroupAllocateMemory(
583
                                               hpiHostApi->allocations, sizeof(PaDeviceInfo*) * deviceCount ),
584
                    paInsufficientMemory );
585
        /* Allocate all device info structs in a contiguous block */
586
        PA_UNLESS_( hpiDeviceList = (PaAsiHpiDeviceInfo*) PaUtil_GroupAllocateMemory(
587
                                        hpiHostApi->allocations, sizeof(PaAsiHpiDeviceInfo) * deviceCount ),
588
                    paInsufficientMemory );
589

    
590
        /* Now query devices again for information */
591
        for( i=0; i < numAdapters; ++i )
592
        {
593
            uint16_t inStreams, outStreams;
594
            uint16_t version;
595
            uint32_t serial;
596
            uint16_t type;
597
            uint32_t idx;
598

    
599
            hpiError = HPI_SubSysGetAdapter( NULL, i, &idx, &type );
600
            if (hpiError)
601
                continue;
602

    
603
            /* Assume adapter is still open from previous round */
604
            hpiError = HPI_AdapterGetInfo( NULL, idx,
605
                                           &outStreams, &inStreams, &version, &serial, &type );
606
            /* Report error and skip to next device on failure */
607
            if( hpiError )
608
            {
609
                PA_ASIHPI_REPORT_ERROR_( hpiError );
610
                continue;
611
            }
612
            else
613
            {
614
                PA_DEBUG(( "Found HPI Adapter ID=%4X Idx=%d #In=%d #Out=%d S/N=%d HWver=%c%d DSPver=%03d\n",
615
                           type, idx, inStreams, outStreams, serial,
616
                           ((version>>3)&0xf)+'A',                  /* Hw version major */
617
                           version&0x7,                             /* Hw version minor */
618
                           ((version>>13)*100)+((version>>7)&0x3f)  /* DSP code version */
619
                         ));
620
            }
621

    
622
            /* First add all input streams as devices */
623
            for( j=0; j < inStreams; ++j )
624
            {
625
                PaAsiHpiDeviceInfo *hpiDevice = &hpiDeviceList[deviceIndex];
626
                PaDeviceInfo *baseDeviceInfo = &hpiDevice->baseDeviceInfo;
627
                char srcName[72];
628
                char *deviceName;
629

    
630
                memset( hpiDevice, 0, sizeof(PaAsiHpiDeviceInfo) );
631
                /* Set implementation-specific device details */
632
                hpiDevice->adapterIndex = idx;
633
                hpiDevice->adapterType = type;
634
                hpiDevice->adapterVersion = version;
635
                hpiDevice->adapterSerialNumber = serial;
636
                hpiDevice->streamIndex = j;
637
                hpiDevice->streamIsOutput = 0;
638
                /* Set common PortAudio device stats */
639
                baseDeviceInfo->structVersion = 2;
640
                /* Make sure name string is owned by API info structure */
641
                sprintf( srcName,
642
                         "Adapter %d (%4X) - Input Stream %d", i+1, type, j+1 );
643
                PA_UNLESS_( deviceName = (char *) PaUtil_GroupAllocateMemory(
644
                                             hpiHostApi->allocations, strlen(srcName) + 1 ), paInsufficientMemory );
645
                strcpy( deviceName, srcName );
646
                baseDeviceInfo->name = deviceName;
647
                baseDeviceInfo->hostApi = hpiHostApi->hostApiIndex;
648
                baseDeviceInfo->maxInputChannels = HPI_MAX_CHANNELS;
649
                baseDeviceInfo->maxOutputChannels = 0;
650
                /* Default latency values for interactive performance */
651
                baseDeviceInfo->defaultLowInputLatency = 0.01;
652
                baseDeviceInfo->defaultLowOutputLatency = -1.0;
653
                /* Default latency values for robust non-interactive applications (eg. playing sound files) */
654
                baseDeviceInfo->defaultHighInputLatency = 0.2;
655
                baseDeviceInfo->defaultHighOutputLatency = -1.0;
656
                /* HPI interface can actually handle any sampling rate to 1 Hz accuracy,
657
                * so this default is as good as any */
658
                baseDeviceInfo->defaultSampleRate = 44100;
659

    
660
                /* Store device in global PortAudio list */
661
                hostApi->deviceInfos[deviceIndex++] = (PaDeviceInfo *) hpiDevice;
662
            }
663

    
664
            /* Now add all output streams as devices (I know, the repetition is painful) */
665
            for( j=0; j < outStreams; ++j )
666
            {
667
                PaAsiHpiDeviceInfo *hpiDevice = &hpiDeviceList[deviceIndex];
668
                PaDeviceInfo *baseDeviceInfo = &hpiDevice->baseDeviceInfo;
669
                char srcName[72];
670
                char *deviceName;
671

    
672
                memset( hpiDevice, 0, sizeof(PaAsiHpiDeviceInfo) );
673
                /* Set implementation-specific device details */
674
                hpiDevice->adapterIndex = idx;
675
                hpiDevice->adapterType = type;
676
                hpiDevice->adapterVersion = version;
677
                hpiDevice->adapterSerialNumber = serial;
678
                hpiDevice->streamIndex = j;
679
                hpiDevice->streamIsOutput = 1;
680
                /* Set common PortAudio device stats */
681
                baseDeviceInfo->structVersion = 2;
682
                /* Make sure name string is owned by API info structure */
683
                sprintf( srcName,
684
                         "Adapter %d (%4X) - Output Stream %d", i+1, type, j+1 );
685
                PA_UNLESS_( deviceName = (char *) PaUtil_GroupAllocateMemory(
686
                                             hpiHostApi->allocations, strlen(srcName) + 1 ), paInsufficientMemory );
687
                strcpy( deviceName, srcName );
688
                baseDeviceInfo->name = deviceName;
689
                baseDeviceInfo->hostApi = hpiHostApi->hostApiIndex;
690
                baseDeviceInfo->maxInputChannels = 0;
691
                baseDeviceInfo->maxOutputChannels = HPI_MAX_CHANNELS;
692
                /* Default latency values for interactive performance. */
693
                baseDeviceInfo->defaultLowInputLatency = -1.0;
694
                baseDeviceInfo->defaultLowOutputLatency = 0.01;
695
                /* Default latency values for robust non-interactive applications (eg. playing sound files). */
696
                baseDeviceInfo->defaultHighInputLatency = -1.0;
697
                baseDeviceInfo->defaultHighOutputLatency = 0.2;
698
                /* HPI interface can actually handle any sampling rate to 1 Hz accuracy,
699
                * so this default is as good as any */
700
                baseDeviceInfo->defaultSampleRate = 44100;
701

    
702
                /* Store device in global PortAudio list */
703
                hostApi->deviceInfos[deviceIndex++] = (PaDeviceInfo *) hpiDevice;
704
            }
705
        }
706
    }
707

    
708
    /* Finally acknowledge checked devices */
709
    baseApiInfo->deviceCount = deviceIndex;
710

    
711
error:
712
    return result;
713
}
714

    
715

    
716
/** Initialize host API implementation.
717
 This is the only function exported beyond this file. It is called by PortAudio to initialize
718
 the host API. It stores API info, finds and registers all devices, and sets up callback and
719
 blocking interfaces.
720

721
 @param hostApi Pointer to host API struct
722

723
 @param hostApiIndex Index of current (HPI) host API
724

725
 @return PortAudio error code
726
 */
727
PaError PaAsiHpi_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex )
728
{
729
    PaError result = paNoError;
730
    PaAsiHpiHostApiRepresentation *hpiHostApi = NULL;
731
    PaHostApiInfo *baseApiInfo;
732

    
733
    /* Try to initialize HPI subsystem */
734
    if (!HPI_SubSysCreate())
735
    {
736
        /* the V19 development docs say that if an implementation
737
         * detects that it cannot be used, it should return a NULL
738
         * interface and paNoError */
739
        PA_DEBUG(( "Could not open HPI interface\n" ));
740

    
741
        *hostApi = NULL;
742
        return paNoError;
743
    }
744
    else
745
    {
746
        uint32_t hpiVersion;
747
        PA_ASIHPI_UNLESS_( HPI_SubSysGetVersionEx( NULL, &hpiVersion ), paUnanticipatedHostError );
748
        PA_DEBUG(( "HPI interface v%d.%02d.%02d\n",
749
                   hpiVersion >> 16,  (hpiVersion >> 8) & 0x0F, (hpiVersion & 0x0F) ));
750
    }
751

    
752
    /* Allocate host API structure */
753
    PA_UNLESS_( hpiHostApi = (PaAsiHpiHostApiRepresentation*) PaUtil_AllocateMemory(
754
                                 sizeof(PaAsiHpiHostApiRepresentation) ), paInsufficientMemory );
755
    PA_UNLESS_( hpiHostApi->allocations = PaUtil_CreateAllocationGroup(), paInsufficientMemory );
756

    
757
    hpiHostApi->hostApiIndex = hostApiIndex;
758

    
759
    *hostApi = &hpiHostApi->baseHostApiRep;
760
    baseApiInfo = &((*hostApi)->info);
761
    /* Fill in common API details */
762
    baseApiInfo->structVersion = 1;
763
    baseApiInfo->type = paAudioScienceHPI;
764
    baseApiInfo->name = "AudioScience HPI";
765
    baseApiInfo->deviceCount = 0;
766
    baseApiInfo->defaultInputDevice = paNoDevice;
767
    baseApiInfo->defaultOutputDevice = paNoDevice;
768

    
769
    PA_ENSURE_( PaAsiHpi_BuildDeviceList( hpiHostApi ) );
770

    
771
    (*hostApi)->Terminate = Terminate;
772
    (*hostApi)->OpenStream = OpenStream;
773
    (*hostApi)->IsFormatSupported = IsFormatSupported;
774

    
775
    PaUtil_InitializeStreamInterface( &hpiHostApi->callbackStreamInterface, CloseStream, StartStream,
776
                                      StopStream, AbortStream, IsStreamStopped, IsStreamActive,
777
                                      GetStreamTime, GetStreamCpuLoad,
778
                                      PaUtil_DummyRead, PaUtil_DummyWrite,
779
                                      PaUtil_DummyGetReadAvailable, PaUtil_DummyGetWriteAvailable );
780

    
781
    PaUtil_InitializeStreamInterface( &hpiHostApi->blockingStreamInterface, CloseStream, StartStream,
782
                                      StopStream, AbortStream, IsStreamStopped, IsStreamActive,
783
                                      GetStreamTime, PaUtil_DummyGetCpuLoad,
784
                                      ReadStream, WriteStream, GetStreamReadAvailable, GetStreamWriteAvailable );
785

    
786
    /* Store identity of main thread */
787
    PA_ENSURE_( PaUnixThreading_Initialize() );
788

    
789
    return result;
790
error:
791
    if (hpiHostApi)
792
        PaUtil_FreeMemory( hpiHostApi );
793
    return result;
794
}
795

    
796

    
797
/** Terminate host API implementation.
798
 This closes all HPI adapters and frees the HPI subsystem. It also frees the host API struct
799
 memory. It should be called once for every PaAsiHpi_Initialize call.
800

801
 @param Pointer to host API struct
802
 */
803
static void Terminate( struct PaUtilHostApiRepresentation *hostApi )
804
{
805
    PaAsiHpiHostApiRepresentation *hpiHostApi = (PaAsiHpiHostApiRepresentation*)hostApi;
806
    int i;
807
    PaError result = paNoError;
808

    
809
    if( hpiHostApi )
810
    {
811
        /* Get rid of HPI-specific structures */
812
        uint16_t lastAdapterIndex = HPI_MAX_ADAPTERS;
813
        /* Iterate through device list and close adapters */
814
        for( i=0; i < hostApi->info.deviceCount; ++i )
815
        {
816
            PaAsiHpiDeviceInfo *hpiDevice = (PaAsiHpiDeviceInfo *) hostApi->deviceInfos[ i ];
817
            /* Close adapter only if it differs from previous one */
818
            if( hpiDevice->adapterIndex != lastAdapterIndex )
819
            {
820
                /* Ignore errors (report only during debugging) */
821
                PA_ASIHPI_UNLESS_( HPI_AdapterClose( NULL,
822
                                                     hpiDevice->adapterIndex ), paNoError );
823
                lastAdapterIndex = hpiDevice->adapterIndex;
824
            }
825
        }
826
        /* Finally dismantle HPI subsystem */
827
        HPI_SubSysFree( NULL );
828

    
829
        if( hpiHostApi->allocations )
830
        {
831
            PaUtil_FreeAllAllocations( hpiHostApi->allocations );
832
            PaUtil_DestroyAllocationGroup( hpiHostApi->allocations );
833
        }
834

    
835
        PaUtil_FreeMemory( hpiHostApi );
836
    }
837
error:
838
    return;
839
}
840

    
841

    
842
/** Converts PortAudio sample format to equivalent HPI format.
843

844
 @param paFormat PortAudio sample format
845

846
 @return HPI sample format
847
 */
848
static uint16_t PaAsiHpi_PaToHpiFormat( PaSampleFormat paFormat )
849
{
850
    /* Ignore interleaving flag */
851
    switch( paFormat & ~paNonInterleaved )
852
    {
853
    case paFloat32:
854
        return HPI_FORMAT_PCM32_FLOAT;
855

    
856
    case paInt32:
857
        return HPI_FORMAT_PCM32_SIGNED;
858

    
859
    case paInt24:
860
        return HPI_FORMAT_PCM24_SIGNED;
861

    
862
    case paInt16:
863
        return HPI_FORMAT_PCM16_SIGNED;
864

    
865
    case paUInt8:
866
        return HPI_FORMAT_PCM8_UNSIGNED;
867

    
868
        /* Default is 16-bit signed */
869
    case paInt8:
870
    default:
871
        return HPI_FORMAT_PCM16_SIGNED;
872
    }
873
}
874

    
875

    
876
/** Converts HPI sample format to equivalent PortAudio format.
877

878
 @param paFormat HPI sample format
879

880
 @return PortAudio sample format
881
 */
882
static PaSampleFormat PaAsiHpi_HpiToPaFormat( uint16_t hpiFormat )
883
{
884
    switch( hpiFormat )
885
    {
886
    case HPI_FORMAT_PCM32_FLOAT:
887
        return paFloat32;
888

    
889
    case HPI_FORMAT_PCM32_SIGNED:
890
        return paInt32;
891

    
892
    case HPI_FORMAT_PCM24_SIGNED:
893
        return paInt24;
894

    
895
    case HPI_FORMAT_PCM16_SIGNED:
896
        return paInt16;
897

    
898
    case HPI_FORMAT_PCM8_UNSIGNED:
899
        return paUInt8;
900

    
901
        /* Default is custom format (e.g. for HPI MP3 format) */
902
    default:
903
        return paCustomFormat;
904
    }
905
}
906

    
907

    
908
/** Creates HPI format struct based on PortAudio parameters.
909
 This also does some checks to see whether the desired format is valid, and whether
910
 the device allows it. This only checks the format of one half (input or output) of the
911
 PortAudio stream.
912

913
 @param hostApi Pointer to host API struct
914

915
 @param parameters Pointer to stream parameter struct
916

917
 @param sampleRate Desired sample rate
918

919
 @param hpiDevice Pointer to HPI device struct
920

921
 @param hpiFormat Resulting HPI format returned here
922

923
 @return PortAudio error code (typically indicating a problem with stream format)
924
 */
925
static PaError PaAsiHpi_CreateFormat( struct PaUtilHostApiRepresentation *hostApi,
926
                                      const PaStreamParameters *parameters, double sampleRate,
927
                                      PaAsiHpiDeviceInfo **hpiDevice, struct hpi_format *hpiFormat )
928
{
929
    int maxChannelCount = 0;
930
    PaSampleFormat hostSampleFormat = 0;
931
    hpi_err_t hpiError = 0;
932

    
933
    /* Unless alternate device specification is supported, reject the use of
934
       paUseHostApiSpecificDeviceSpecification */
935
    if( parameters->device == paUseHostApiSpecificDeviceSpecification )
936
        return paInvalidDevice;
937
    else
938
    {
939
        assert( parameters->device < hostApi->info.deviceCount );
940
        *hpiDevice = (PaAsiHpiDeviceInfo*) hostApi->deviceInfos[ parameters->device ];
941
    }
942

    
943
    /* Validate streamInfo - this implementation doesn't use custom stream info */
944
    if( parameters->hostApiSpecificStreamInfo )
945
        return paIncompatibleHostApiSpecificStreamInfo;
946

    
947
    /* Check that device can support channel count */
948
    if( (*hpiDevice)->streamIsOutput )
949
    {
950
        maxChannelCount = (*hpiDevice)->baseDeviceInfo.maxOutputChannels;
951
    }
952
    else
953
    {
954
        maxChannelCount = (*hpiDevice)->baseDeviceInfo.maxInputChannels;
955
    }
956
    if( (maxChannelCount == 0) || (parameters->channelCount > maxChannelCount) )
957
        return paInvalidChannelCount;
958

    
959
    /* All standard sample formats are supported by the buffer adapter,
960
       and this implementation doesn't support any custom sample formats */
961
    if( parameters->sampleFormat & paCustomFormat )
962
        return paSampleFormatNotSupported;
963

    
964
    /* Switch to closest HPI native format */
965
    hostSampleFormat = PaUtil_SelectClosestAvailableFormat(PA_ASIHPI_AVAILABLE_FORMATS_,
966
                       parameters->sampleFormat );
967
    /* Setup format + info objects */
968
    hpiError = HPI_FormatCreate( hpiFormat, (uint16_t)parameters->channelCount,
969
                                 PaAsiHpi_PaToHpiFormat( hostSampleFormat ),
970
                                 (uint32_t)sampleRate, 0, 0 );
971
    if( hpiError )
972
    {
973
        PA_ASIHPI_REPORT_ERROR_( hpiError );
974
        switch( hpiError )
975
        {
976
        case HPI_ERROR_INVALID_FORMAT:
977
            return paSampleFormatNotSupported;
978

    
979
        case HPI_ERROR_INVALID_SAMPLERATE:
980
        case HPI_ERROR_INCOMPATIBLE_SAMPLERATE:
981
            return paInvalidSampleRate;
982

    
983
        case HPI_ERROR_INVALID_CHANNELS:
984
            return paInvalidChannelCount;
985
        }
986
    }
987

    
988
    return paNoError;
989
}
990

    
991

    
992
/** Open HPI input stream with given format.
993
 This attempts to open HPI input stream with desired format. If the format is not supported
994
 or the device is unavailable, the stream is closed and a PortAudio error code is returned.
995

996
 @param hostApi Pointer to host API struct
997

998
 @param hpiDevice Pointer to HPI device struct
999

1000
 @param hpiFormat Pointer to HPI format struct
1001

1002
 @return PortAudio error code (typically indicating a problem with stream format or device)
1003
*/
1004
static PaError PaAsiHpi_OpenInput( struct PaUtilHostApiRepresentation *hostApi,
1005
                                   const PaAsiHpiDeviceInfo *hpiDevice, const struct hpi_format *hpiFormat,
1006
                                   hpi_handle_t *hpiStream )
1007
{
1008
    PaAsiHpiHostApiRepresentation *hpiHostApi = (PaAsiHpiHostApiRepresentation*)hostApi;
1009
    PaError result = paNoError;
1010
    hpi_err_t hpiError = 0;
1011

    
1012
    /* Catch misplaced output devices, as they typically have 0 input channels */
1013
    PA_UNLESS_( !hpiDevice->streamIsOutput, paInvalidChannelCount );
1014
    /* Try to open input stream */
1015
    PA_ASIHPI_UNLESS_( HPI_InStreamOpen( NULL, hpiDevice->adapterIndex,
1016
                                         hpiDevice->streamIndex, hpiStream ), paDeviceUnavailable );
1017
    /* Set input format (checking it in the process) */
1018
    /* Could also use HPI_InStreamQueryFormat, but this economizes the process */
1019
    hpiError = HPI_InStreamSetFormat( NULL, *hpiStream, (struct hpi_format*)hpiFormat );
1020
    if( hpiError )
1021
    {
1022
        PA_ASIHPI_REPORT_ERROR_( hpiError );
1023
        PA_ASIHPI_UNLESS_( HPI_InStreamClose( NULL, *hpiStream ), paNoError );
1024
        switch( hpiError )
1025
        {
1026
        case HPI_ERROR_INVALID_FORMAT:
1027
            return paSampleFormatNotSupported;
1028

    
1029
        case HPI_ERROR_INVALID_SAMPLERATE:
1030
        case HPI_ERROR_INCOMPATIBLE_SAMPLERATE:
1031
            return paInvalidSampleRate;
1032

    
1033
        case HPI_ERROR_INVALID_CHANNELS:
1034
            return paInvalidChannelCount;
1035

    
1036
        default:
1037
            /* In case anything else went wrong */
1038
            return paInvalidDevice;
1039
        }
1040
    }
1041

    
1042
error:
1043
    return result;
1044
}
1045

    
1046

    
1047
/** Open HPI output stream with given format.
1048
 This attempts to open HPI output stream with desired format. If the format is not supported
1049
 or the device is unavailable, the stream is closed and a PortAudio error code is returned.
1050

1051
 @param hostApi Pointer to host API struct
1052

1053
 @param hpiDevice Pointer to HPI device struct
1054

1055
 @param hpiFormat Pointer to HPI format struct
1056

1057
 @return PortAudio error code (typically indicating a problem with stream format or device)
1058
*/
1059
static PaError PaAsiHpi_OpenOutput( struct PaUtilHostApiRepresentation *hostApi,
1060
                                    const PaAsiHpiDeviceInfo *hpiDevice, const struct hpi_format *hpiFormat,
1061
                                    hpi_handle_t *hpiStream )
1062
{
1063
    PaAsiHpiHostApiRepresentation *hpiHostApi = (PaAsiHpiHostApiRepresentation*)hostApi;
1064
    PaError result = paNoError;
1065
    hpi_err_t hpiError = 0;
1066

    
1067
    /* Catch misplaced input devices, as they typically have 0 output channels */
1068
    PA_UNLESS_( hpiDevice->streamIsOutput, paInvalidChannelCount );
1069
    /* Try to open output stream */
1070
    PA_ASIHPI_UNLESS_( HPI_OutStreamOpen( NULL, hpiDevice->adapterIndex,
1071
                                          hpiDevice->streamIndex, hpiStream ), paDeviceUnavailable );
1072

    
1073
    /* Check output format (format is set on first write to output stream) */
1074
    hpiError = HPI_OutStreamQueryFormat( NULL, *hpiStream, (struct hpi_format*)hpiFormat );
1075
    if( hpiError )
1076
    {
1077
        PA_ASIHPI_REPORT_ERROR_( hpiError );
1078
        PA_ASIHPI_UNLESS_( HPI_OutStreamClose( NULL, *hpiStream ), paNoError );
1079
        switch( hpiError )
1080
        {
1081
        case HPI_ERROR_INVALID_FORMAT:
1082
            return paSampleFormatNotSupported;
1083

    
1084
        case HPI_ERROR_INVALID_SAMPLERATE:
1085
        case HPI_ERROR_INCOMPATIBLE_SAMPLERATE:
1086
            return paInvalidSampleRate;
1087

    
1088
        case HPI_ERROR_INVALID_CHANNELS:
1089
            return paInvalidChannelCount;
1090

    
1091
        default:
1092
            /* In case anything else went wrong */
1093
            return paInvalidDevice;
1094
        }
1095
    }
1096

    
1097
error:
1098
    return result;
1099
}
1100

    
1101

    
1102
/** Checks whether the desired stream formats and devices are supported
1103
 (for both input and output).
1104
 This is done by actually opening the appropriate HPI streams and closing them again.
1105

1106
 @param hostApi Pointer to host API struct
1107

1108
 @param inputParameters Pointer to stream parameter struct for input side of stream
1109

1110
 @param outputParameters Pointer to stream parameter struct for output side of stream
1111

1112
 @param sampleRate Desired sample rate
1113

1114
 @return PortAudio error code (paFormatIsSupported on success)
1115
 */
1116
static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
1117
                                  const PaStreamParameters *inputParameters,
1118
                                  const PaStreamParameters *outputParameters,
1119
                                  double sampleRate )
1120
{
1121
    PaError result = paFormatIsSupported;
1122
    PaAsiHpiHostApiRepresentation *hpiHostApi = (PaAsiHpiHostApiRepresentation*)hostApi;
1123
    PaAsiHpiDeviceInfo *hpiDevice = NULL;
1124
    struct hpi_format hpiFormat;
1125

    
1126
    /* Input stream */
1127
    if( inputParameters )
1128
    {
1129
        hpi_handle_t hpiStream;
1130
        PA_DEBUG(( "%s: Checking input params: dev=%d, sr=%d, chans=%d, fmt=%d\n",
1131
                   __FUNCTION__, inputParameters->device, (int)sampleRate,
1132
                   inputParameters->channelCount, inputParameters->sampleFormat ));
1133
        /* Create and validate format */
1134
        PA_ENSURE_( PaAsiHpi_CreateFormat( hostApi, inputParameters, sampleRate,
1135
                                           &hpiDevice, &hpiFormat ) );
1136
        /* Open stream to further check format */
1137
        PA_ENSURE_( PaAsiHpi_OpenInput( hostApi, hpiDevice, &hpiFormat, &hpiStream ) );
1138
        /* Close stream again */
1139
        PA_ASIHPI_UNLESS_( HPI_InStreamClose( NULL, hpiStream ), paNoError );
1140
    }
1141

    
1142
    /* Output stream */
1143
    if( outputParameters )
1144
    {
1145
        hpi_handle_t hpiStream;
1146
        PA_DEBUG(( "%s: Checking output params: dev=%d, sr=%d, chans=%d, fmt=%d\n",
1147
                   __FUNCTION__, outputParameters->device, (int)sampleRate,
1148
                   outputParameters->channelCount, outputParameters->sampleFormat ));
1149
        /* Create and validate format */
1150
        PA_ENSURE_( PaAsiHpi_CreateFormat( hostApi, outputParameters, sampleRate,
1151
                                           &hpiDevice, &hpiFormat ) );
1152
        /* Open stream to further check format */
1153
        PA_ENSURE_( PaAsiHpi_OpenOutput( hostApi, hpiDevice, &hpiFormat, &hpiStream ) );
1154
        /* Close stream again */
1155
        PA_ASIHPI_UNLESS_( HPI_OutStreamClose( NULL, hpiStream ), paNoError );
1156
    }
1157

    
1158
error:
1159
    return result;
1160
}
1161

    
1162
/* ---------------------------- Stream Interface ---------------------------- */
1163

    
1164
/** Obtain HPI stream information.
1165
 This obtains info such as stream state and available data/space in buffers. It also
1166
 estimates whether an underflow or overflow occurred.
1167

1168
 @param streamComp Pointer to stream component (input or output) to query
1169

1170
 @param info Pointer to stream info struct that will contain result
1171

1172
 @return PortAudio error code (either paNoError, paDeviceUnavailable or paUnanticipatedHostError)
1173
 */
1174
static PaError PaAsiHpi_GetStreamInfo( PaAsiHpiStreamComponent *streamComp, PaAsiHpiStreamInfo *info )
1175
{
1176
    PaError result = paDeviceUnavailable;
1177
    uint16_t state;
1178
    uint32_t bufferSize, dataSize, frameCounter, auxDataSize, threshold;
1179
    uint32_t hwBufferSize, hwDataSize;
1180

    
1181
    assert( streamComp );
1182
    assert( info );
1183

    
1184
    /* First blank the stream info struct, in case something goes wrong below.
1185
       This saves the caller from initializing the struct. */
1186
    info->state = 0;
1187
    info->bufferSize = 0;
1188
    info->dataSize = 0;
1189
    info->frameCounter = 0;
1190
    info->auxDataSize = 0;
1191
    info->totalBufferedData = 0;
1192
    info->availableFrames = 0;
1193
    info->underflow = 0;
1194
    info->overflow = 0;
1195

    
1196
    if( streamComp->hpiDevice && streamComp->hpiStream )
1197
    {
1198
        /* Obtain detailed stream info (either input or output) */
1199
        if( streamComp->hpiDevice->streamIsOutput )
1200
        {
1201
            PA_ASIHPI_UNLESS_( HPI_OutStreamGetInfoEx( NULL,
1202
                               streamComp->hpiStream,
1203
                               &state, &bufferSize, &dataSize, &frameCounter,
1204
                               &auxDataSize ), paUnanticipatedHostError );
1205
        }
1206
        else
1207
        {
1208
            PA_ASIHPI_UNLESS_( HPI_InStreamGetInfoEx( NULL,
1209
                               streamComp->hpiStream,
1210
                               &state, &bufferSize, &dataSize, &frameCounter,
1211
                               &auxDataSize ), paUnanticipatedHostError );
1212
        }
1213
        /* Load stream info */
1214
        info->state = state;
1215
        info->bufferSize = bufferSize;
1216
        info->dataSize = dataSize;
1217
        info->frameCounter = frameCounter;
1218
        info->auxDataSize = auxDataSize;
1219
        /* Determine total buffered data */
1220
        info->totalBufferedData = dataSize;
1221
        if( streamComp->hostBufferSize > 0 )
1222
            info->totalBufferedData += auxDataSize;
1223
        info->totalBufferedData /= streamComp->bytesPerFrame;
1224
        /* Determine immediately available frames */
1225
        info->availableFrames = streamComp->hpiDevice->streamIsOutput ?
1226
                                bufferSize - dataSize : dataSize;
1227
        info->availableFrames /= streamComp->bytesPerFrame;
1228
        /* Minimum space/data required in buffers */
1229
        threshold = PA_MIN( streamComp->tempBufferSize,
1230
                            streamComp->bytesPerFrame * PA_ASIHPI_MIN_FRAMES_ );
1231
        /* Obtain hardware buffer stats first, to simplify things */
1232
        hwBufferSize = streamComp->hardwareBufferSize;
1233
        hwDataSize = streamComp->hostBufferSize > 0 ? auxDataSize : dataSize;
1234
        /* Underflow is a bit tricky */
1235
        info->underflow = streamComp->hpiDevice->streamIsOutput ?
1236
                          /* Stream seems to start in drained state sometimes, so ignore initial underflow */
1237
                          (frameCounter > 0) && ( (state == HPI_STATE_DRAINED) || (hwDataSize == 0) ) :
1238
                          /* Input streams check the first-level (host) buffer for underflow */
1239
                          (state != HPI_STATE_STOPPED) && (dataSize < threshold);
1240
        /* Check for overflow in second-level (hardware) buffer for both input and output */
1241
        info->overflow = (state != HPI_STATE_STOPPED) && (hwBufferSize - hwDataSize < threshold);
1242

    
1243
        return paNoError;
1244
    }
1245

    
1246
error:
1247
    return result;
1248
}
1249

    
1250

    
1251
/** Display stream component information for debugging purposes.
1252

1253
 @param streamComp Pointer to stream component (input or output) to query
1254

1255
 @param stream Pointer to stream struct which contains the component above
1256
 */
1257
static void PaAsiHpi_StreamComponentDump( PaAsiHpiStreamComponent *streamComp,
1258
        PaAsiHpiStream *stream )
1259
{
1260
    PaAsiHpiStreamInfo streamInfo;
1261

    
1262
    assert( streamComp );
1263
    assert( stream );
1264

    
1265
    /* Name of soundcard/device used by component */
1266
    PA_DEBUG(( "device: %s\n", streamComp->hpiDevice->baseDeviceInfo.name ));
1267
    /* Unfortunately some overlap between input and output here */
1268
    if( streamComp->hpiDevice->streamIsOutput )
1269
    {
1270
        /* Settings on the user side (as experienced by user callback) */
1271
        PA_DEBUG(( "user: %d-bit, %d ",
1272
                   8*stream->bufferProcessor.bytesPerUserOutputSample,
1273
                   stream->bufferProcessor.outputChannelCount));
1274
        if( stream->bufferProcessor.userOutputIsInterleaved )
1275
        {
1276
            PA_DEBUG(( "interleaved channels, " ));
1277
        }
1278
        else
1279
        {
1280
            PA_DEBUG(( "non-interleaved channels, " ));
1281
        }
1282
        PA_DEBUG(( "%d frames/buffer, latency = %5.1f ms\n",
1283
                   stream->bufferProcessor.framesPerUserBuffer,
1284
                   1000*stream->baseStreamRep.streamInfo.outputLatency ));
1285
        /* Settings on the host side (internal to PortAudio host API) */
1286
        PA_DEBUG(( "host: %d-bit, %d interleaved channels, %d frames/buffer ",
1287
                   8*stream->bufferProcessor.bytesPerHostOutputSample,
1288
                   stream->bufferProcessor.outputChannelCount,
1289
                   stream->bufferProcessor.framesPerHostBuffer ));
1290
    }
1291
    else
1292
    {
1293
        /* Settings on the user side (as experienced by user callback) */
1294
        PA_DEBUG(( "user: %d-bit, %d ",
1295
                   8*stream->bufferProcessor.bytesPerUserInputSample,
1296
                   stream->bufferProcessor.inputChannelCount));
1297
        if( stream->bufferProcessor.userInputIsInterleaved )
1298
        {
1299
            PA_DEBUG(( "interleaved channels, " ));
1300
        }
1301
        else
1302
        {
1303
            PA_DEBUG(( "non-interleaved channels, " ));
1304
        }
1305
        PA_DEBUG(( "%d frames/buffer, latency = %5.1f ms\n",
1306
                   stream->bufferProcessor.framesPerUserBuffer,
1307
                   1000*stream->baseStreamRep.streamInfo.inputLatency ));
1308
        /* Settings on the host side (internal to PortAudio host API) */
1309
        PA_DEBUG(( "host: %d-bit, %d interleaved channels, %d frames/buffer ",
1310
                   8*stream->bufferProcessor.bytesPerHostInputSample,
1311
                   stream->bufferProcessor.inputChannelCount,
1312
                   stream->bufferProcessor.framesPerHostBuffer ));
1313
    }
1314
    switch( stream->bufferProcessor.hostBufferSizeMode )
1315
    {
1316
    case paUtilFixedHostBufferSize:
1317
        PA_DEBUG(( "[fixed] " ));
1318
        break;
1319
    case paUtilBoundedHostBufferSize:
1320
        PA_DEBUG(( "[bounded] " ));
1321
        break;
1322
    case paUtilUnknownHostBufferSize:
1323
        PA_DEBUG(( "[unknown] " ));
1324
        break;
1325
    case paUtilVariableHostBufferSizePartialUsageAllowed:
1326
        PA_DEBUG(( "[variable] " ));
1327
        break;
1328
    }
1329
    PA_DEBUG(( "(%d max)\n", streamComp->tempBufferSize / streamComp->bytesPerFrame ));
1330
    /* HPI hardware settings */
1331
    PA_DEBUG(( "HPI: adapter %d stream %d, %d-bit, %d-channel, %d Hz\n",
1332
               streamComp->hpiDevice->adapterIndex, streamComp->hpiDevice->streamIndex,
1333
               8 * streamComp->bytesPerFrame / streamComp->hpiFormat.wChannels,
1334
               streamComp->hpiFormat.wChannels,
1335
               streamComp->hpiFormat.dwSampleRate ));
1336
    /* Stream state and buffer levels */
1337
    PA_DEBUG(( "HPI: " ));
1338
    PaAsiHpi_GetStreamInfo( streamComp, &streamInfo );
1339
    switch( streamInfo.state )
1340
    {
1341
    case HPI_STATE_STOPPED:
1342
        PA_DEBUG(( "[STOPPED] " ));
1343
        break;
1344
    case HPI_STATE_PLAYING:
1345
        PA_DEBUG(( "[PLAYING] " ));
1346
        break;
1347
    case HPI_STATE_RECORDING:
1348
        PA_DEBUG(( "[RECORDING] " ));
1349
        break;
1350
    case HPI_STATE_DRAINED:
1351
        PA_DEBUG(( "[DRAINED] " ));
1352
        break;
1353
    default:
1354
        PA_DEBUG(( "[unknown state] " ));
1355
        break;
1356
    }
1357
    if( streamComp->hostBufferSize )
1358
    {
1359
        PA_DEBUG(( "host = %d/%d B, ", streamInfo.dataSize, streamComp->hostBufferSize ));
1360
        PA_DEBUG(( "hw = %d/%d (%d) B, ", streamInfo.auxDataSize,
1361
                   streamComp->hardwareBufferSize, streamComp->outputBufferCap ));
1362
    }
1363
    else
1364
    {
1365
        PA_DEBUG(( "hw = %d/%d B, ", streamInfo.dataSize, streamComp->hardwareBufferSize ));
1366
    }
1367
    PA_DEBUG(( "count = %d", streamInfo.frameCounter ));
1368
    if( streamInfo.overflow )
1369
    {
1370
        PA_DEBUG(( " [overflow]" ));
1371
    }
1372
    else if( streamInfo.underflow )
1373
    {
1374
        PA_DEBUG(( " [underflow]" ));
1375
    }
1376
    PA_DEBUG(( "\n" ));
1377
}
1378

    
1379

    
1380
/** Display stream information for debugging purposes.
1381

1382
 @param stream Pointer to stream to query
1383
 */
1384
static void PaAsiHpi_StreamDump( PaAsiHpiStream *stream )
1385
{
1386
    assert( stream );
1387

    
1388
    PA_DEBUG(( "\n------------------------- STREAM INFO FOR %p ---------------------------\n", stream ));
1389
    /* General stream info (input+output) */
1390
    if( stream->baseStreamRep.streamCallback )
1391
    {
1392
        PA_DEBUG(( "[callback] " ));
1393
    }
1394
    else
1395
    {
1396
        PA_DEBUG(( "[blocking] " ));
1397
    }
1398
    PA_DEBUG(( "sr=%d Hz, poll=%d ms, max %d frames/buf ",
1399
               (int)stream->baseStreamRep.streamInfo.sampleRate,
1400
               stream->pollingInterval, stream->maxFramesPerHostBuffer ));
1401
    switch( stream->state )
1402
    {
1403
    case paAsiHpiStoppedState:
1404
        PA_DEBUG(( "[stopped]\n" ));
1405
        break;
1406
    case paAsiHpiActiveState:
1407
        PA_DEBUG(( "[active]\n" ));
1408
        break;
1409
    case paAsiHpiCallbackFinishedState:
1410
        PA_DEBUG(( "[cb fin]\n" ));
1411
        break;
1412
    default:
1413
        PA_DEBUG(( "[unknown state]\n" ));
1414
        break;
1415
    }
1416
    if( stream->callbackMode )
1417
    {
1418
        PA_DEBUG(( "cb info: thread=%p, cbAbort=%d, cbFinished=%d\n",
1419
                   stream->thread.thread, stream->callbackAbort, stream->callbackFinished ));
1420
    }
1421

    
1422
    PA_DEBUG(( "----------------------------------- Input  ------------------------------------\n" ));
1423
    if( stream->input )
1424
    {
1425
        PaAsiHpi_StreamComponentDump( stream->input, stream );
1426
    }
1427
    else
1428
    {
1429
        PA_DEBUG(( "*none*\n" ));
1430
    }
1431

    
1432
    PA_DEBUG(( "----------------------------------- Output ------------------------------------\n" ));
1433
    if( stream->output )
1434
    {
1435
        PaAsiHpi_StreamComponentDump( stream->output, stream );
1436
    }
1437
    else
1438
    {
1439
        PA_DEBUG(( "*none*\n" ));
1440
    }
1441
    PA_DEBUG(( "-------------------------------------------------------------------------------\n\n" ));
1442

    
1443
}
1444

    
1445

    
1446
/** Determine buffer sizes and allocate appropriate stream buffers.
1447
 This attempts to allocate a BBM (host) buffer for the HPI stream component (either input
1448
 or output, as both have similar buffer needs). Not all AudioScience adapters support BBM,
1449
 in which case the hardware buffer has to suffice. The size of the HPI host buffer is chosen
1450
 as a multiple of framesPerPaHostBuffer, and also influenced by the suggested latency and the
1451
 estimated minimum polling interval. The HPI host and hardware buffer sizes are stored, and an
1452
 appropriate cap for the hardware buffer is also calculated. Finally, the temporary stream
1453
 buffer which serves as the PortAudio host buffer for this implementation is allocated.
1454
 This buffer contains an integer number of user buffers, to simplify buffer adaption in the
1455
 buffer processor. The function returns paBufferTooBig if the HPI interface cannot allocate
1456
 an HPI host buffer of the desired size.
1457

1458
 @param streamComp Pointer to stream component struct
1459

1460
 @param pollingInterval Polling interval for stream, in milliseconds
1461

1462
 @param framesPerPaHostBuffer Size of PortAudio host buffer, in frames
1463

1464
 @param suggestedLatency Suggested latency for stream component, in seconds
1465

1466
 @return PortAudio error code (possibly paBufferTooBig or paInsufficientMemory)
1467
 */
1468
static PaError PaAsiHpi_SetupBuffers( PaAsiHpiStreamComponent *streamComp, uint32_t pollingInterval,
1469
                                      unsigned long framesPerPaHostBuffer, PaTime suggestedLatency )
1470
{
1471
    PaError result = paNoError;
1472
    PaAsiHpiStreamInfo streamInfo;
1473
    unsigned long hpiBufferSize = 0, paHostBufferSize = 0;
1474

    
1475
    assert( streamComp );
1476
    assert( streamComp->hpiDevice );
1477

    
1478
    /* Obtain size of hardware buffer of HPI stream, since we will be activating BBM shortly
1479
       and afterwards the buffer size will refer to the BBM (host-side) buffer.
1480
       This is necessary to enable reliable detection of xruns. */
1481
    PA_ENSURE_( PaAsiHpi_GetStreamInfo( streamComp, &streamInfo ) );
1482
    streamComp->hardwareBufferSize = streamInfo.bufferSize;
1483
    hpiBufferSize = streamInfo.bufferSize;
1484

    
1485
    /* Check if BBM (background bus mastering) is to be enabled */
1486
    if( PA_ASIHPI_USE_BBM_ )
1487
    {
1488
        uint32_t bbmBufferSize = 0, preLatencyBufferSize = 0;
1489
        hpi_err_t hpiError = 0;
1490
        PaTime pollingOverhead;
1491

    
1492
        /* Check overhead of Pa_Sleep() call (minimum sleep duration in ms -> OS dependent) */
1493
        pollingOverhead = PaUtil_GetTime();
1494
        Pa_Sleep( 0 );
1495
        pollingOverhead = 1000*(PaUtil_GetTime() - pollingOverhead);
1496
        PA_DEBUG(( "polling overhead = %f ms (length of 0-second sleep)\n", pollingOverhead ));
1497
        /* Obtain minimum recommended size for host buffer (in bytes) */
1498
        PA_ASIHPI_UNLESS_( HPI_StreamEstimateBufferSize( &streamComp->hpiFormat,
1499
                           pollingInterval + (uint32_t)ceil( pollingOverhead ),
1500
                           &bbmBufferSize ), paUnanticipatedHostError );
1501
        /* BBM places more stringent requirements on buffer size (see description */
1502
        /* of HPI_StreamEstimateBufferSize in HPI API document) */
1503
        bbmBufferSize *= 3;
1504
        /* Make sure the BBM buffer contains multiple PA host buffers */
1505
        if( bbmBufferSize < 3 * streamComp->bytesPerFrame * framesPerPaHostBuffer )
1506
            bbmBufferSize = 3 * streamComp->bytesPerFrame * framesPerPaHostBuffer;
1507
        /* Try to honor latency suggested by user by growing buffer (no decrease possible) */
1508
        if( suggestedLatency > 0.0 )
1509
        {
1510
            PaTime bufferDuration = ((PaTime)bbmBufferSize) / streamComp->bytesPerFrame
1511
                                    / streamComp->hpiFormat.dwSampleRate;
1512
            /* Don't decrease buffer */
1513
            if( bufferDuration < suggestedLatency )
1514
            {
1515
                /* Save old buffer size, to be retried if new size proves too big */
1516
                preLatencyBufferSize = bbmBufferSize;
1517
                bbmBufferSize = (uint32_t)ceil( suggestedLatency * streamComp->bytesPerFrame
1518
                                            * streamComp->hpiFormat.dwSampleRate );
1519
            }
1520
        }
1521
        /* Choose closest memory block boundary (HPI API document states that
1522
        "a buffer size of Nx4096 - 20 makes the best use of memory"
1523
        (under the entry for HPI_StreamEstimateBufferSize)) */
1524
        bbmBufferSize = ((uint32_t)ceil((bbmBufferSize + 20)/4096.0))*4096 - 20;
1525
        streamComp->hostBufferSize = bbmBufferSize;
1526
        /* Allocate BBM host buffer (this enables bus mastering transfers in background) */
1527
        if( streamComp->hpiDevice->streamIsOutput )
1528
            hpiError = HPI_OutStreamHostBufferAllocate( NULL,
1529
                       streamComp->hpiStream,
1530
                       bbmBufferSize );
1531
        else
1532
            hpiError = HPI_InStreamHostBufferAllocate( NULL,
1533
                       streamComp->hpiStream,
1534
                       bbmBufferSize );
1535
        if( hpiError )
1536
        {
1537
            /* Indicate that BBM is disabled */
1538
            streamComp->hostBufferSize = 0;
1539
            /* Retry with smaller buffer size (transfers will still work, but not via BBM) */
1540
            if( hpiError == HPI_ERROR_INVALID_DATASIZE )
1541
            {
1542
                /* Retry BBM allocation with smaller size if requested latency proved too big */
1543
                if( preLatencyBufferSize > 0 )
1544
                {
1545
                    PA_DEBUG(( "Retrying BBM allocation with smaller size (%d vs. %d bytes)\n",
1546
                               preLatencyBufferSize, bbmBufferSize ));
1547
                    bbmBufferSize = preLatencyBufferSize;
1548
                    if( streamComp->hpiDevice->streamIsOutput )
1549
                        hpiError = HPI_OutStreamHostBufferAllocate( NULL,
1550
                                   streamComp->hpiStream,
1551
                                   bbmBufferSize );
1552
                    else
1553
                        hpiError = HPI_InStreamHostBufferAllocate( NULL,
1554
                                   streamComp->hpiStream,
1555
                                   bbmBufferSize );
1556
                    /* Another round of error checking */
1557
                    if( hpiError )
1558
                    {
1559
                        PA_ASIHPI_REPORT_ERROR_( hpiError );
1560
                        /* No escapes this time */
1561
                        if( hpiError == HPI_ERROR_INVALID_DATASIZE )
1562
                        {
1563
                            result = paBufferTooBig;
1564
                            goto error;
1565
                        }
1566
                        else if( hpiError != HPI_ERROR_INVALID_OPERATION )
1567
                        {
1568
                            result = paUnanticipatedHostError;
1569
                            goto error;
1570
                        }
1571
                    }
1572
                    else
1573
                    {
1574
                        streamComp->hostBufferSize = bbmBufferSize;
1575
                        hpiBufferSize = bbmBufferSize;
1576
                    }
1577
                }
1578
                else
1579
                {
1580
                    result = paBufferTooBig;
1581
                    goto error;
1582
                }
1583
            }
1584
            /* If BBM not supported, foreground transfers will be used, but not a show-stopper */
1585
            /* Anything else is an error */
1586
            else if (( hpiError != HPI_ERROR_INVALID_OPERATION ) &&
1587
                     ( hpiError != HPI_ERROR_INVALID_FUNC ))
1588
            {
1589
                PA_ASIHPI_REPORT_ERROR_( hpiError );
1590
                result = paUnanticipatedHostError;
1591
                goto error;
1592
            }
1593
        }
1594
        else
1595
        {
1596
            hpiBufferSize = bbmBufferSize;
1597
        }
1598
    }
1599

    
1600
    /* Final check of buffer size */
1601
    paHostBufferSize = streamComp->bytesPerFrame * framesPerPaHostBuffer;
1602
    if( hpiBufferSize < 3*paHostBufferSize )
1603
    {
1604
        result = paBufferTooBig;
1605
        goto error;
1606
    }
1607
    /* Set cap on output buffer size, based on latency suggestions */
1608
    if( streamComp->hpiDevice->streamIsOutput )
1609
    {
1610
        PaTime latency = suggestedLatency > 0.0 ? suggestedLatency :
1611
                         streamComp->hpiDevice->baseDeviceInfo.defaultHighOutputLatency;
1612
        streamComp->outputBufferCap =
1613
            (uint32_t)ceil( latency * streamComp->bytesPerFrame * streamComp->hpiFormat.dwSampleRate );
1614
        /* The cap should not be too small, to prevent underflow */
1615
        if( streamComp->outputBufferCap < 4*paHostBufferSize )
1616
            streamComp->outputBufferCap = 4*paHostBufferSize;
1617
    }
1618
    else
1619
    {
1620
        streamComp->outputBufferCap = 0;
1621
    }
1622
    /* Temp buffer size should be multiple of PA host buffer size (or 1x, if using fixed blocks) */
1623
    streamComp->tempBufferSize = paHostBufferSize;
1624
    /* Allocate temp buffer */
1625
    PA_UNLESS_( streamComp->tempBuffer = (uint8_t *)PaUtil_AllocateMemory( streamComp->tempBufferSize ),
1626
                paInsufficientMemory );
1627
error:
1628
    return result;
1629
}
1630

    
1631

    
1632
/** Opens PortAudio stream.
1633
 This determines a suitable value for framesPerBuffer, if the user didn't specify it,
1634
 based on the suggested latency. It then opens each requested stream direction with the
1635
 appropriate stream format, and allocates the required stream buffers. It sets up the
1636
 various PortAudio structures dealing with streams, and estimates the stream latency.
1637

1638
 See pa_hostapi.h for a list of validity guarantees made about OpenStream parameters.
1639

1640
 @param hostApi Pointer to host API struct
1641

1642
 @param s List of open streams, where successfully opened stream will go
1643

1644
 @param inputParameters Pointer to stream parameter struct for input side of stream
1645

1646
 @param outputParameters Pointer to stream parameter struct for output side of stream
1647

1648
 @param sampleRate Desired sample rate
1649

1650
 @param framesPerBuffer Desired number of frames per buffer passed to user callback
1651
                        (or chunk size for blocking stream)
1652

1653
 @param streamFlags Stream flags
1654

1655
 @param streamCallback Pointer to user callback function (zero for blocking interface)
1656

1657
 @param userData Pointer to user data that will be passed to callback function along with data
1658

1659
 @return PortAudio error code
1660
*/
1661
static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
1662
                           PaStream **s,
1663
                           const PaStreamParameters *inputParameters,
1664
                           const PaStreamParameters *outputParameters,
1665
                           double sampleRate,
1666
                           unsigned long framesPerBuffer,
1667
                           PaStreamFlags streamFlags,
1668
                           PaStreamCallback *streamCallback,
1669
                           void *userData )
1670
{
1671
    PaError result = paNoError;
1672
    PaAsiHpiHostApiRepresentation *hpiHostApi = (PaAsiHpiHostApiRepresentation*)hostApi;
1673
    PaAsiHpiStream *stream = NULL;
1674
    unsigned long framesPerHostBuffer = framesPerBuffer;
1675
    int inputChannelCount = 0, outputChannelCount = 0;
1676
    PaSampleFormat inputSampleFormat = 0, outputSampleFormat = 0;
1677
    PaSampleFormat hostInputSampleFormat = 0, hostOutputSampleFormat = 0;
1678
    PaTime maxSuggestedLatency = 0.0;
1679

    
1680
    /* Validate platform-specific flags -> none expected for HPI */
1681
    if( (streamFlags & paPlatformSpecificFlags) != 0 )
1682
        return paInvalidFlag; /* unexpected platform-specific flag */
1683

    
1684
    /* Create blank stream structure */
1685
    PA_UNLESS_( stream = (PaAsiHpiStream *)PaUtil_AllocateMemory( sizeof(PaAsiHpiStream) ),
1686
                paInsufficientMemory );
1687
    memset( stream, 0, sizeof(PaAsiHpiStream) );
1688

    
1689
    /* If the number of frames per buffer is unspecified, we have to come up with one. */
1690
    if( framesPerHostBuffer == paFramesPerBufferUnspecified )
1691
    {
1692
        if( inputParameters )
1693
            maxSuggestedLatency = inputParameters->suggestedLatency;
1694
        if( outputParameters && (outputParameters->suggestedLatency > maxSuggestedLatency) )
1695
            maxSuggestedLatency = outputParameters->suggestedLatency;
1696
        /* Use suggested latency if available */
1697
        if( maxSuggestedLatency > 0.0 )
1698
            framesPerHostBuffer = (unsigned long)ceil( maxSuggestedLatency * sampleRate );
1699
        else
1700
            /* AudioScience cards like BIG buffers by default */
1701
            framesPerHostBuffer = 4096;
1702
    }
1703
    /* Lower bounds on host buffer size, due to polling and HPI constraints */
1704
    if( 1000.0*framesPerHostBuffer/sampleRate < PA_ASIHPI_MIN_POLLING_INTERVAL_ )
1705
        framesPerHostBuffer = (unsigned long)ceil( sampleRate * PA_ASIHPI_MIN_POLLING_INTERVAL_ / 1000.0 );
1706
    /*    if( framesPerHostBuffer < PA_ASIHPI_MIN_FRAMES_ )
1707
            framesPerHostBuffer = PA_ASIHPI_MIN_FRAMES_; */
1708
    /* Efficient if host buffer size is integer multiple of user buffer size */
1709
    if( framesPerBuffer > 0 )
1710
        framesPerHostBuffer = (unsigned long)ceil( (double)framesPerHostBuffer / framesPerBuffer ) * framesPerBuffer;
1711
    /* Buffer should always be a multiple of 4 bytes to facilitate 32-bit PCI transfers.
1712
     By keeping the frames a multiple of 4, this is ensured even for 8-bit mono sound. */
1713
    framesPerHostBuffer = (framesPerHostBuffer / 4) * 4;
1714
    /* Polling is based on time length (in milliseconds) of user-requested block size */
1715
    stream->pollingInterval = (uint32_t)ceil( 1000.0*framesPerHostBuffer/sampleRate );
1716
    assert( framesPerHostBuffer > 0 );
1717

    
1718
    /* Open underlying streams, check formats and allocate buffers */
1719
    if( inputParameters )
1720
    {
1721
        /* Create blank stream component structure */
1722
        PA_UNLESS_( stream->input = (PaAsiHpiStreamComponent *)PaUtil_AllocateMemory( sizeof(PaAsiHpiStreamComponent) ),
1723
                    paInsufficientMemory );
1724
        memset( stream->input, 0, sizeof(PaAsiHpiStreamComponent) );
1725
        /* Create/validate format */
1726
        PA_ENSURE_( PaAsiHpi_CreateFormat( hostApi, inputParameters, sampleRate,
1727
                                           &stream->input->hpiDevice, &stream->input->hpiFormat ) );
1728
        /* Open stream and set format */
1729
        PA_ENSURE_( PaAsiHpi_OpenInput( hostApi, stream->input->hpiDevice, &stream->input->hpiFormat,
1730
                                        &stream->input->hpiStream ) );
1731
        inputChannelCount = inputParameters->channelCount;
1732
        inputSampleFormat = inputParameters->sampleFormat;
1733
        hostInputSampleFormat = PaAsiHpi_HpiToPaFormat( stream->input->hpiFormat.wFormat );
1734
        stream->input->bytesPerFrame = inputChannelCount * Pa_GetSampleSize( hostInputSampleFormat );
1735
        assert( stream->input->bytesPerFrame > 0 );
1736
        /* Allocate host and temp buffers of appropriate size */
1737
        PA_ENSURE_( PaAsiHpi_SetupBuffers( stream->input, stream->pollingInterval,
1738
                                           framesPerHostBuffer, inputParameters->suggestedLatency ) );
1739
    }
1740
    if( outputParameters )
1741
    {
1742
        /* Create blank stream component structure */
1743
        PA_UNLESS_( stream->output = (PaAsiHpiStreamComponent *)PaUtil_AllocateMemory( sizeof(PaAsiHpiStreamComponent) ),
1744
                    paInsufficientMemory );
1745
        memset( stream->output, 0, sizeof(PaAsiHpiStreamComponent) );
1746
        /* Create/validate format */
1747
        PA_ENSURE_( PaAsiHpi_CreateFormat( hostApi, outputParameters, sampleRate,
1748
                                           &stream->output->hpiDevice, &stream->output->hpiFormat ) );
1749
        /* Open stream and check format */
1750
        PA_ENSURE_( PaAsiHpi_OpenOutput( hostApi, stream->output->hpiDevice,
1751
                                         &stream->output->hpiFormat,
1752
                                         &stream->output->hpiStream ) );
1753
        outputChannelCount = outputParameters->channelCount;
1754
        outputSampleFormat = outputParameters->sampleFormat;
1755
        hostOutputSampleFormat = PaAsiHpi_HpiToPaFormat( stream->output->hpiFormat.wFormat );
1756
        stream->output->bytesPerFrame = outputChannelCount * Pa_GetSampleSize( hostOutputSampleFormat );
1757
        /* Allocate host and temp buffers of appropriate size */
1758
        PA_ENSURE_( PaAsiHpi_SetupBuffers( stream->output, stream->pollingInterval,
1759
                                           framesPerHostBuffer, outputParameters->suggestedLatency ) );
1760
    }
1761

    
1762
    /* Determine maximum frames per host buffer (least common denominator of input/output) */
1763
    if( inputParameters && outputParameters )
1764
    {
1765
        stream->maxFramesPerHostBuffer = PA_MIN( stream->input->tempBufferSize / stream->input->bytesPerFrame,
1766
                                         stream->output->tempBufferSize / stream->output->bytesPerFrame );
1767
    }
1768
    else
1769
    {
1770
        stream->maxFramesPerHostBuffer = inputParameters ? stream->input->tempBufferSize / stream->input->bytesPerFrame
1771
                                         : stream->output->tempBufferSize / stream->output->bytesPerFrame;
1772
    }
1773
    assert( stream->maxFramesPerHostBuffer > 0 );
1774
    /* Initialize various other stream parameters */
1775
    stream->neverDropInput = streamFlags & paNeverDropInput;
1776
    stream->state = paAsiHpiStoppedState;
1777

    
1778
    /* Initialize either callback or blocking interface */
1779
    if( streamCallback )
1780
    {
1781
        PaUtil_InitializeStreamRepresentation( &stream->baseStreamRep,
1782
                                               &hpiHostApi->callbackStreamInterface,
1783
                                               streamCallback, userData );
1784
        stream->callbackMode = 1;
1785
    }
1786
    else
1787
    {
1788
        PaUtil_InitializeStreamRepresentation( &stream->baseStreamRep,
1789
                                               &hpiHostApi->blockingStreamInterface,
1790
                                               streamCallback, userData );
1791
        /* Pre-allocate non-interleaved user buffer pointers for blocking interface */
1792
        PA_UNLESS_( stream->blockingUserBufferCopy =
1793
                        PaUtil_AllocateMemory( sizeof(void *) * PA_MAX( inputChannelCount, outputChannelCount ) ),
1794
                    paInsufficientMemory );
1795
        stream->callbackMode = 0;
1796
    }
1797
    PaUtil_InitializeCpuLoadMeasurer( &stream->cpuLoadMeasurer, sampleRate );
1798

    
1799
    /* Following pa_linux_alsa's lead, we operate with fixed host buffer size by default, */
1800
    /* since other modes will invariably lead to block adaption (maybe Bounded better?) */
1801
    PA_ENSURE_( PaUtil_InitializeBufferProcessor( &stream->bufferProcessor,
1802
                inputChannelCount, inputSampleFormat, hostInputSampleFormat,
1803
                outputChannelCount, outputSampleFormat, hostOutputSampleFormat,
1804
                sampleRate, streamFlags,
1805
                framesPerBuffer, framesPerHostBuffer, paUtilFixedHostBufferSize,
1806
                streamCallback, userData ) );
1807

    
1808
    stream->baseStreamRep.streamInfo.structVersion = 1;
1809
    stream->baseStreamRep.streamInfo.sampleRate = sampleRate;
1810
    /* Determine input latency from buffer processor and buffer sizes */
1811
    if( stream->input )
1812
    {
1813
        PaTime bufferDuration = ( stream->input->hostBufferSize + stream->input->hardwareBufferSize )
1814
                                / sampleRate / stream->input->bytesPerFrame;
1815
        stream->baseStreamRep.streamInfo.inputLatency =
1816
            bufferDuration +
1817
            ((PaTime)PaUtil_GetBufferProcessorInputLatencyFrames( &stream->bufferProcessor ) -
1818
                stream->maxFramesPerHostBuffer) / sampleRate;
1819
        assert( stream->baseStreamRep.streamInfo.inputLatency > 0.0 );
1820
    }
1821
    /* Determine output latency from buffer processor and buffer sizes */
1822
    if( stream->output )
1823
    {
1824
        PaTime bufferDuration = ( stream->output->hostBufferSize + stream->output->hardwareBufferSize )
1825
                                / sampleRate / stream->output->bytesPerFrame;
1826
        /* Take buffer size cap into account (see PaAsiHpi_WaitForFrames) */
1827
        if( !stream->input && (stream->output->outputBufferCap > 0) )
1828
        {
1829
            bufferDuration = PA_MIN( bufferDuration,
1830
                                     stream->output->outputBufferCap / sampleRate / stream->output->bytesPerFrame );
1831
        }
1832
        stream->baseStreamRep.streamInfo.outputLatency =
1833
            bufferDuration +
1834
            ((PaTime)PaUtil_GetBufferProcessorOutputLatencyFrames( &stream->bufferProcessor ) -
1835
                stream->maxFramesPerHostBuffer) / sampleRate;
1836
        assert( stream->baseStreamRep.streamInfo.outputLatency > 0.0 );
1837
    }
1838

    
1839
    /* Report stream info, for debugging purposes */
1840
    PaAsiHpi_StreamDump( stream );
1841

    
1842
    /* Save initialized stream to PA stream list */
1843
    *s = (PaStream*)stream;
1844
    return result;
1845

    
1846
error:
1847
    CloseStream( (PaStream*)stream );
1848
    return result;
1849
}
1850

    
1851

    
1852
/** Close PortAudio stream.
1853
 When CloseStream() is called, the multi-api layer ensures that the stream has already
1854
 been stopped or aborted. This closes the underlying HPI streams and deallocates stream
1855
 buffers and structs.
1856

1857
 @param s Pointer to PortAudio stream
1858

1859
 @return PortAudio error code
1860
*/
1861
static PaError CloseStream( PaStream *s )
1862
{
1863
    PaError result = paNoError;
1864
    PaAsiHpiStream *stream = (PaAsiHpiStream*)s;
1865

    
1866
    /* If stream is already gone, all is well */
1867
    if( stream == NULL )
1868
        return paNoError;
1869

    
1870
    /* Generic stream cleanup */
1871
    PaUtil_TerminateBufferProcessor( &stream->bufferProcessor );
1872
    PaUtil_TerminateStreamRepresentation( &stream->baseStreamRep );
1873

    
1874
    /* Implementation-specific details - close internal streams */
1875
    if( stream->input )
1876
    {
1877
        /* Close HPI stream (freeing BBM host buffer in the process, if used) */
1878
        if( stream->input->hpiStream )
1879
        {
1880
            PA_ASIHPI_UNLESS_( HPI_InStreamClose( NULL,
1881
                                                  stream->input->hpiStream ), paUnanticipatedHostError );
1882
        }
1883
        /* Free temp buffer and stream component */
1884
        PaUtil_FreeMemory( stream->input->tempBuffer );
1885
        PaUtil_FreeMemory( stream->input );
1886
    }
1887
    if( stream->output )
1888
    {
1889
        /* Close HPI stream (freeing BBM host buffer in the process, if used) */
1890
        if( stream->output->hpiStream )
1891
        {
1892
            PA_ASIHPI_UNLESS_( HPI_OutStreamClose( NULL,
1893
                                                   stream->output->hpiStream ), paUnanticipatedHostError );
1894
        }
1895
        /* Free temp buffer and stream component */
1896
        PaUtil_FreeMemory( stream->output->tempBuffer );
1897
        PaUtil_FreeMemory( stream->output );
1898
    }
1899

    
1900
    PaUtil_FreeMemory( stream->blockingUserBufferCopy );
1901
    PaUtil_FreeMemory( stream );
1902

    
1903
error:
1904
    return result;
1905
}
1906

    
1907

    
1908
/** Prime HPI output stream with silence.
1909
 This resets the output stream and uses PortAudio helper routines to fill the
1910
 temp buffer with silence. It then writes two host buffers to the stream. This is supposed
1911
 to be called before the stream is started. It has no effect on input-only streams.
1912

1913
 @param stream Pointer to stream struct
1914

1915
 @return PortAudio error code
1916
 */
1917
static PaError PaAsiHpi_PrimeOutputWithSilence( PaAsiHpiStream *stream )
1918
{
1919
    PaError result = paNoError;
1920
    PaAsiHpiStreamComponent *out;
1921
    PaUtilZeroer *zeroer;
1922
    PaSampleFormat outputFormat;
1923
    assert( stream );
1924
    out = stream->output;
1925
    /* Only continue if stream has output channels */
1926
    if( !out )
1927
        return result;
1928
    assert( out->tempBuffer );
1929

    
1930
    /* Clear all existing data in hardware playback buffer */
1931
    PA_ASIHPI_UNLESS_( HPI_OutStreamReset( NULL,
1932
                                           out->hpiStream ), paUnanticipatedHostError );
1933
    /* Fill temp buffer with silence */
1934
    outputFormat = PaAsiHpi_HpiToPaFormat( out->hpiFormat.wFormat );
1935
    zeroer = PaUtil_SelectZeroer( outputFormat );
1936
    zeroer(out->tempBuffer, 1, out->tempBufferSize / Pa_GetSampleSize(outputFormat) );
1937
    /* Write temp buffer to hardware fifo twice, to get started */
1938
    PA_ASIHPI_UNLESS_( HPI_OutStreamWriteBuf( NULL, out->hpiStream,
1939
                                              out->tempBuffer, out->tempBufferSize, &out->hpiFormat),
1940
                                              paUnanticipatedHostError );
1941
    PA_ASIHPI_UNLESS_( HPI_OutStreamWriteBuf( NULL, out->hpiStream,
1942
                                              out->tempBuffer, out->tempBufferSize, &out->hpiFormat),
1943
                                              paUnanticipatedHostError );
1944
error:
1945
    return result;
1946
}
1947

    
1948

    
1949
/** Start HPI streams (both input + output).
1950
 This starts all HPI streams in the PortAudio stream. Output streams are first primed with
1951
 silence, if required. After this call the PA stream is in the Active state.
1952

1953
 @todo Implement priming via the user callback
1954

1955
 @param stream Pointer to stream struct
1956

1957
 @param outputPrimed True if output is already primed (if false, silence will be loaded before starting)
1958

1959
 @return PortAudio error code
1960
 */
1961
static PaError PaAsiHpi_StartStream( PaAsiHpiStream *stream, int outputPrimed )
1962
{
1963
    PaError result = paNoError;
1964

    
1965
    if( stream->input )
1966
    {
1967
        PA_ASIHPI_UNLESS_( HPI_InStreamStart( NULL,
1968
                                              stream->input->hpiStream ), paUnanticipatedHostError );
1969
    }
1970
    if( stream->output )
1971
    {
1972
        if( !outputPrimed )
1973
        {
1974
            /* Buffer isn't primed, so load stream with silence */
1975
            PA_ENSURE_( PaAsiHpi_PrimeOutputWithSilence( stream ) );
1976
        }
1977
        PA_ASIHPI_UNLESS_( HPI_OutStreamStart( NULL,
1978
                                               stream->output->hpiStream ), paUnanticipatedHostError );
1979
    }
1980
    stream->state = paAsiHpiActiveState;
1981
    stream->callbackFinished = 0;
1982

    
1983
    /* Report stream info for debugging purposes */
1984
    /*    PaAsiHpi_StreamDump( stream );   */
1985

    
1986
error:
1987
    return result;
1988
}
1989

    
1990

    
1991
/** Start PortAudio stream.
1992
 If the stream has a callback interface, this starts a helper thread to feed the user callback.
1993
 The thread will then take care of starting the HPI streams, and this function will block
1994
 until the streams actually start. In the case of a blocking interface, the HPI streams
1995
 are simply started.
1996

1997
 @param s Pointer to PortAudio stream
1998

1999
 @return PortAudio error code
2000
*/
2001
static PaError StartStream( PaStream *s )
2002
{
2003
    PaError result = paNoError;
2004
    PaAsiHpiStream *stream = (PaAsiHpiStream*)s;
2005

    
2006
    assert( stream );
2007

    
2008
    /* Ready the processor */
2009
    PaUtil_ResetBufferProcessor( &stream->bufferProcessor );
2010

    
2011
    if( stream->callbackMode )
2012
    {
2013
        /* Create and start callback engine thread */
2014
        /* Also waits 1 second for stream to be started by engine thread (otherwise aborts) */
2015
        PA_ENSURE_( PaUnixThread_New( &stream->thread, &CallbackThreadFunc, stream, 1., 0 /*rtSched*/ ) );
2016
    }
2017
    else
2018
    {
2019
        PA_ENSURE_( PaAsiHpi_StartStream( stream, 0 ) );
2020
    }
2021

    
2022
error:
2023
    return result;
2024
}
2025

    
2026

    
2027
/** Stop HPI streams (input + output), either softly or abruptly.
2028
 If abort is false, the function blocks until the output stream is drained, otherwise it
2029
 stops immediately and discards data in the stream hardware buffers.
2030

2031
 This function is safe to call from the callback engine thread as well as the main thread.
2032

2033
 @param stream Pointer to stream struct
2034

2035
 @param abort True if samples in output buffer should be discarded (otherwise blocks until stream is done)
2036

2037
 @return PortAudio error code
2038

2039
 */
2040
static PaError PaAsiHpi_StopStream( PaAsiHpiStream *stream, int abort )
2041
{
2042
    PaError result = paNoError;
2043

    
2044
    assert( stream );
2045

    
2046
    /* Input channels */
2047
    if( stream->input )
2048
    {
2049
        PA_ASIHPI_UNLESS_( HPI_InStreamReset( NULL,
2050
                                              stream->input->hpiStream ), paUnanticipatedHostError );
2051
    }
2052
    /* Output channels */
2053
    if( stream->output )
2054
    {
2055
        if( !abort )
2056
        {
2057
            /* Wait until HPI output stream is drained */
2058
            while( 1 )
2059
            {
2060
                PaAsiHpiStreamInfo streamInfo;
2061
                PaTime timeLeft;
2062

    
2063
                /* Obtain number of samples waiting to be played */
2064
                PA_ENSURE_( PaAsiHpi_GetStreamInfo( stream->output, &streamInfo ) );
2065
                /* Check if stream is drained */
2066
                if( (streamInfo.state != HPI_STATE_PLAYING) &&
2067
                        (streamInfo.dataSize < stream->output->bytesPerFrame * PA_ASIHPI_MIN_FRAMES_) )
2068
                    break;
2069
                /* Sleep amount of time represented by remaining samples */
2070
                timeLeft = 1000.0 * streamInfo.dataSize / stream->output->bytesPerFrame
2071
                           / stream->baseStreamRep.streamInfo.sampleRate;
2072
                Pa_Sleep( (long)ceil( timeLeft ) );
2073
            }
2074
        }
2075
        PA_ASIHPI_UNLESS_( HPI_OutStreamReset( NULL,
2076
                                               stream->output->hpiStream ), paUnanticipatedHostError );
2077
    }
2078

    
2079
    /* Report stream info for debugging purposes */
2080
    /*    PaAsiHpi_StreamDump( stream ); */
2081

    
2082
error:
2083
    return result;
2084
}
2085

    
2086

    
2087
/** Stop or abort PortAudio stream.
2088

2089
 This function is used to explicitly stop the PortAudio stream (via StopStream/AbortStream),
2090
 as opposed to the situation when the callback finishes with a result other than paContinue.
2091
 If a stream is in callback mode we will have to inspect whether the background thread has
2092
 finished, or we will have to take it out. In either case we join the thread before returning.
2093
 In blocking mode, we simply tell HPI to stop abruptly (abort) or finish buffers (drain).
2094
 The PortAudio stream will be in the Stopped state after a call to this function.
2095

2096
 Don't call this from the callback engine thread!
2097

2098
 @param stream Pointer to stream struct
2099

2100
 @param abort True if samples in output buffer should be discarded (otherwise blocks until stream is done)
2101

2102
 @return PortAudio error code
2103
*/
2104
static PaError PaAsiHpi_ExplicitStop( PaAsiHpiStream *stream, int abort )
2105
{
2106
    PaError result = paNoError;
2107

    
2108
    /* First deal with the callback thread, cancelling and/or joining it if necessary */
2109
    if( stream->callbackMode )
2110
    {
2111
        PaError threadRes;
2112
        stream->callbackAbort = abort;
2113
        if( abort )
2114
        {
2115
            PA_DEBUG(( "Aborting callback\n" ));
2116
        }
2117
        else
2118
        {
2119
            PA_DEBUG(( "Stopping callback\n" ));
2120
        }
2121
        PA_ENSURE_( PaUnixThread_Terminate( &stream->thread, !abort, &threadRes ) );
2122
        if( threadRes != paNoError )
2123
        {
2124
            PA_DEBUG(( "Callback thread returned: %d\n", threadRes ));
2125
        }
2126
    }
2127
    else
2128
    {
2129
        PA_ENSURE_( PaAsiHpi_StopStream( stream, abort ) );
2130
    }
2131

    
2132
    stream->state = paAsiHpiStoppedState;
2133

    
2134
error:
2135
    return result;
2136
}
2137

    
2138

    
2139
/** Stop PortAudio stream.
2140
 This blocks until the output buffers are drained.
2141

2142
 @param s Pointer to PortAudio stream
2143

2144
 @return PortAudio error code
2145
*/
2146
static PaError StopStream( PaStream *s )
2147
{
2148
    return PaAsiHpi_ExplicitStop( (PaAsiHpiStream *) s, 0 );
2149
}
2150

    
2151

    
2152
/** Abort PortAudio stream.
2153
 This discards any existing data in output buffers and stops the stream immediately.
2154

2155
 @param s Pointer to PortAudio stream
2156

2157
 @return PortAudio error code
2158
*/
2159
static PaError AbortStream( PaStream *s )
2160
{
2161
    return PaAsiHpi_ExplicitStop( (PaAsiHpiStream * ) s, 1 );
2162
}
2163

    
2164

    
2165
/** Determine whether the stream is stopped.
2166
 A stream is considered to be stopped prior to a successful call to StartStream and after
2167
 a successful call to StopStream or AbortStream. If a stream callback returns a value other
2168
 than paContinue the stream is NOT considered to be stopped (it is in CallbackFinished state).
2169

2170
 @param s Pointer to PortAudio stream
2171

2172
 @return Returns one (1) when the stream is stopped, zero (0) when the stream is running, or
2173
         a PaErrorCode (which are always negative) if PortAudio is not initialized or an
2174
         error is encountered.
2175
*/
2176
static PaError IsStreamStopped( PaStream *s )
2177
{
2178
    PaAsiHpiStream *stream = (PaAsiHpiStream*)s;
2179

    
2180
    assert( stream );
2181
    return stream->state == paAsiHpiStoppedState ? 1 : 0;
2182
}
2183

    
2184

    
2185
/** Determine whether the stream is active.
2186
 A stream is active after a successful call to StartStream(), until it becomes inactive either
2187
 as a result of a call to StopStream() or AbortStream(), or as a result of a return value
2188
 other than paContinue from the stream callback. In the latter case, the stream is considered
2189
 inactive after the last buffer has finished playing.
2190

2191
 @param s Pointer to PortAudio stream
2192

2193
 @return Returns one (1) when the stream is active (i.e. playing or recording audio),
2194
         zero (0) when not playing, or a PaErrorCode (which are always negative)
2195
         if PortAudio is not initialized or an error is encountered.
2196
*/
2197
static PaError IsStreamActive( PaStream *s )
2198
{
2199
    PaAsiHpiStream *stream = (PaAsiHpiStream*)s;
2200

    
2201
    assert( stream );
2202
    return stream->state == paAsiHpiActiveState ? 1 : 0;
2203
}
2204

    
2205

    
2206
/** Returns current stream time.
2207
 This corresponds to the system clock. The clock should run continuously while the stream
2208
 is open, i.e. between calls to OpenStream() and CloseStream(), therefore a frame counter
2209
 is not good enough.
2210

2211
 @param s Pointer to PortAudio stream
2212

2213
 @return Stream time, in seconds
2214
 */
2215
static PaTime GetStreamTime( PaStream *s )
2216
{
2217
    return PaUtil_GetTime();
2218
}
2219

    
2220

    
2221
/** Returns CPU load.
2222

2223
 @param s Pointer to PortAudio stream
2224

2225
 @return CPU load (0.0 if blocking interface is used)
2226
 */
2227
static double GetStreamCpuLoad( PaStream *s )
2228
{
2229
    PaAsiHpiStream *stream = (PaAsiHpiStream*)s;
2230

    
2231
    return stream->callbackMode ? PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer ) : 0.0;
2232
}
2233

    
2234
/* --------------------------- Callback Interface --------------------------- */
2235

    
2236
/** Exit routine which is called when callback thread quits.
2237
 This takes care of stopping the HPI streams (either waiting for output to finish, or
2238
 abruptly). It also calls the user-supplied StreamFinished callback, and sets the
2239
 stream state to CallbackFinished if it was reached via a non-paContinue return from
2240
 the user callback function.
2241

2242
 @param userData A pointer to an open stream previously created with Pa_OpenStream
2243
 */
2244
static void PaAsiHpi_OnThreadExit( void *userData )
2245
{
2246
    PaAsiHpiStream *stream = (PaAsiHpiStream *) userData;
2247

    
2248
    assert( stream );
2249

    
2250
    PaUtil_ResetCpuLoadMeasurer( &stream->cpuLoadMeasurer );
2251

    
2252
    PA_DEBUG(( "%s: Stopping HPI streams\n", __FUNCTION__ ));
2253
    PaAsiHpi_StopStream( stream, stream->callbackAbort );
2254
    PA_DEBUG(( "%s: Stoppage\n", __FUNCTION__ ));
2255

    
2256
    /* Eventually notify user all buffers have played */
2257
    if( stream->baseStreamRep.streamFinishedCallback )
2258
    {
2259
        stream->baseStreamRep.streamFinishedCallback( stream->baseStreamRep.userData );
2260
    }
2261

    
2262
    /* Unfortunately both explicit calls to Stop/AbortStream (leading to Stopped state)
2263
     and implicit stops via paComplete/paAbort (leading to CallbackFinished state)
2264
     end up here - need another flag to remind us which is the case */
2265
    if( stream->callbackFinished )
2266
        stream->state = paAsiHpiCallbackFinishedState;
2267
}
2268

    
2269

    
2270
/** Wait until there is enough frames to fill a host buffer.
2271
 The routine attempts to sleep until at least a full host buffer can be retrieved from the
2272
 input HPI stream and passed to the output HPI stream. It will first sleep until enough
2273
 output space is available, as this is usually easily achievable. If it is an output-only
2274
 stream, it will also sleep if the hardware buffer is too full, thereby throttling the
2275
 filling of the output buffer and reducing output latency. The routine then blocks until
2276
 enough input samples are available, unless this will cause an output underflow. In the
2277
 process, input overflows and output underflows are indicated.
2278

2279
 @param stream Pointer to stream struct
2280

2281
 @param framesAvail Returns the number of available frames
2282

2283
 @param cbFlags Overflows and underflows indicated in here
2284

2285
 @return PortAudio error code (only paUnanticipatedHostError expected)
2286
 */
2287
static PaError PaAsiHpi_WaitForFrames( PaAsiHpiStream *stream, unsigned long *framesAvail,
2288
                                       PaStreamCallbackFlags *cbFlags )
2289
{
2290
    PaError result = paNoError;
2291
    double sampleRate;
2292
    unsigned long framesTarget;
2293
    uint32_t outputData = 0, outputSpace = 0, inputData = 0, framesLeft = 0;
2294

    
2295
    assert( stream );
2296
    assert( stream->input || stream->output );
2297

    
2298
    sampleRate = stream->baseStreamRep.streamInfo.sampleRate;
2299
    /* We have to come up with this much frames on both input and output */
2300
    framesTarget = stream->bufferProcessor.framesPerHostBuffer;
2301
    assert( framesTarget > 0 );
2302

    
2303
    while( 1 )
2304
    {
2305
        PaAsiHpiStreamInfo info;
2306
        /* Check output first, as this takes priority in the default full-duplex mode */
2307
        if( stream->output )
2308
        {
2309
            PA_ENSURE_( PaAsiHpi_GetStreamInfo( stream->output, &info ) );
2310
            /* Wait until enough space is available in output buffer to receive a full block */
2311
            if( info.availableFrames < framesTarget )
2312
            {
2313
                framesLeft = framesTarget - info.availableFrames;
2314
                Pa_Sleep( (long)ceil( 1000 * framesLeft / sampleRate ) );
2315
                continue;
2316
            }
2317
            /* Wait until the data in hardware buffer has dropped to a sensible level.
2318
             Without this, the hardware buffer quickly fills up in the absence of an input
2319
             stream to regulate its data rate (if data generation is fast). This leads to
2320
             large latencies, as the AudioScience hardware buffers are humongous.
2321
             This is similar to the default "Hardware Buffering=off" option in the
2322
             AudioScience WAV driver. */
2323
            if( !stream->input && (stream->output->outputBufferCap > 0) &&
2324
                    ( info.totalBufferedData > stream->output->outputBufferCap / stream->output->bytesPerFrame ) )
2325
            {
2326
                framesLeft = info.totalBufferedData - stream->output->outputBufferCap / stream->output->bytesPerFrame;
2327
                Pa_Sleep( (long)ceil( 1000 * framesLeft / sampleRate ) );
2328
                continue;
2329
            }
2330
            outputData = info.totalBufferedData;
2331
            outputSpace = info.availableFrames;
2332
            /* Report output underflow to callback */
2333
            if( info.underflow )
2334
            {
2335
                *cbFlags |= paOutputUnderflow;
2336
            }
2337
        }
2338

    
2339
        /* Now check input side */
2340
        if( stream->input )
2341
        {
2342
            PA_ENSURE_( PaAsiHpi_GetStreamInfo( stream->input, &info ) );
2343
            /* If a full block of samples hasn't been recorded yet, wait for it if possible */
2344
            if( info.availableFrames < framesTarget )
2345
            {
2346
                framesLeft = framesTarget - info.availableFrames;
2347
                /* As long as output is not disrupted in the process, wait for a full
2348
                block of input samples */
2349
                if( !stream->output || (outputData > framesLeft) )
2350
                {
2351
                    Pa_Sleep( (long)ceil( 1000 * framesLeft / sampleRate ) );
2352
                    continue;
2353
                }
2354
            }
2355
            inputData = info.availableFrames;
2356
            /** @todo The paInputOverflow flag should be set in the callback containing the
2357
             first input sample following the overflow. That means the block currently sitting
2358
             at the fore-front of recording, i.e. typically the one containing the newest (last)
2359
             sample in the HPI buffer system. This is most likely not the same as the current
2360
             block of data being passed to the callback. The current overflow should ideally
2361
             be noted in an overflow list of sorts, with an indication of when it should be
2362
             reported. The trouble starts if there are several separate overflow incidents,
2363
             given a big input buffer. Oh well, something to try out later... */
2364
            if( info.overflow )
2365
            {
2366
                *cbFlags |= paInputOverflow;
2367
            }
2368
        }
2369
        break;
2370
    }
2371
    /* Full-duplex stream */
2372
    if( stream->input && stream->output )
2373
    {
2374
        if( outputSpace >= framesTarget )
2375
            *framesAvail = outputSpace;
2376
        /* If input didn't make the target, keep the output count instead (input underflow) */
2377
        if( (inputData >= framesTarget) && (inputData < outputSpace) )
2378
            *framesAvail = inputData;
2379
    }
2380
    else
2381
    {
2382
        *framesAvail = stream->input ? inputData : outputSpace;
2383
    }
2384

    
2385
error:
2386
    return result;
2387
}
2388

    
2389

    
2390
/** Obtain recording, current and playback timestamps of stream.
2391
 The current time is determined by the system clock. This "now" timestamp occurs at the
2392
 forefront of recording (and playback in the full-duplex case), which happens later than the
2393
 input timestamp by an amount equal to the total number of recorded frames in the input buffer.
2394
 The output timestamp indicates when the next generated sample will actually be played. This
2395
 happens after all the samples currently in the output buffer are played. The output timestamp
2396
 therefore follows the current timestamp by an amount equal to the number of frames yet to be
2397
 played back in the output buffer.
2398

2399
 If the current timestamp is the present, the input timestamp is in the past and the output
2400
 timestamp is in the future.
2401

2402
 @param stream Pointer to stream struct
2403

2404
 @param timeInfo Pointer to timeInfo struct that will contain timestamps
2405
 */
2406
static void PaAsiHpi_CalculateTimeInfo( PaAsiHpiStream *stream, PaStreamCallbackTimeInfo *timeInfo )
2407
{
2408
    PaAsiHpiStreamInfo streamInfo;
2409
    double sampleRate;
2410

    
2411
    assert( stream );
2412
    assert( timeInfo );
2413
    sampleRate = stream->baseStreamRep.streamInfo.sampleRate;
2414

    
2415
    /* The current time ("now") is at the forefront of both recording and playback */
2416
    timeInfo->currentTime = GetStreamTime( (PaStream *)stream );
2417
    /* The last sample in the input buffer was recorded just now, so the first sample
2418
     happened (number of recorded samples)/sampleRate ago */
2419
    timeInfo->inputBufferAdcTime = timeInfo->currentTime;
2420
    if( stream->input )
2421
    {
2422
        PaAsiHpi_GetStreamInfo( stream->input, &streamInfo );
2423
        timeInfo->inputBufferAdcTime -= streamInfo.totalBufferedData / sampleRate;
2424
    }
2425
    /* The first of the outgoing samples will be played after all the samples in the output
2426
     buffer is done */
2427
    timeInfo->outputBufferDacTime = timeInfo->currentTime;
2428
    if( stream->output )
2429
    {
2430
        PaAsiHpi_GetStreamInfo( stream->output, &streamInfo );
2431
        timeInfo->outputBufferDacTime += streamInfo.totalBufferedData / sampleRate;
2432
    }
2433
}
2434

    
2435

    
2436
/** Read from HPI input stream and register buffers.
2437
 This reads data from the HPI input stream (if it exists) and registers the temp stream
2438
 buffers of both input and output streams with the buffer processor. In the process it also
2439
 handles input underflows in the full-duplex case.
2440

2441
 @param stream Pointer to stream struct
2442

2443
 @param numFrames On entrance the number of available frames, on exit the number of
2444
                  received frames
2445

2446
 @param cbFlags Indicates overflows and underflows
2447

2448
 @return PortAudio error code
2449
 */
2450
static PaError PaAsiHpi_BeginProcessing( PaAsiHpiStream *stream, unsigned long *numFrames,
2451
        PaStreamCallbackFlags *cbFlags )
2452
{
2453
    PaError result = paNoError;
2454

    
2455
    assert( stream );
2456
    if( *numFrames > stream->maxFramesPerHostBuffer )
2457
        *numFrames = stream->maxFramesPerHostBuffer;
2458

    
2459
    if( stream->input )
2460
    {
2461
        PaAsiHpiStreamInfo info;
2462

    
2463
        uint32_t framesToGet = *numFrames;
2464

    
2465
        /* Check for overflows and underflows yet again */
2466
        PA_ENSURE_( PaAsiHpi_GetStreamInfo( stream->input, &info ) );
2467
        if( info.overflow )
2468
        {
2469
            *cbFlags |= paInputOverflow;
2470
        }
2471
        /* Input underflow if less than expected number of samples pitch up */
2472
        if( framesToGet > info.availableFrames )
2473
        {
2474
            PaUtilZeroer *zeroer;
2475
            PaSampleFormat inputFormat;
2476

    
2477
            /* Never call an input-only stream with InputUnderflow set */
2478
            if( stream->output )
2479
                *cbFlags |= paInputUnderflow;
2480
            framesToGet = info.availableFrames;
2481
            /* Fill temp buffer with silence (to make up for missing input samples) */
2482
            inputFormat = PaAsiHpi_HpiToPaFormat( stream->input->hpiFormat.wFormat );
2483
            zeroer = PaUtil_SelectZeroer( inputFormat );
2484
            zeroer(stream->input->tempBuffer, 1,
2485
                   stream->input->tempBufferSize / Pa_GetSampleSize(inputFormat) );
2486
        }
2487

    
2488
        /* Read block of data into temp buffer */
2489
        PA_ASIHPI_UNLESS_( HPI_InStreamReadBuf( NULL,
2490
                                             stream->input->hpiStream,
2491
                                             stream->input->tempBuffer,
2492
                                             framesToGet * stream->input->bytesPerFrame),
2493
                           paUnanticipatedHostError );
2494
        /* Register temp buffer with buffer processor (always FULL buffer) */
2495
        PaUtil_SetInputFrameCount( &stream->bufferProcessor, *numFrames );
2496
        /* HPI interface only allows interleaved channels */
2497
        PaUtil_SetInterleavedInputChannels( &stream->bufferProcessor,
2498
                                            0, stream->input->tempBuffer,
2499
                                            stream->input->hpiFormat.wChannels );
2500
    }
2501
    if( stream->output )
2502
    {
2503
        /* Register temp buffer with buffer processor */
2504
        PaUtil_SetOutputFrameCount( &stream->bufferProcessor, *numFrames );
2505
        /* HPI interface only allows interleaved channels */
2506
        PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor,
2507
                                             0, stream->output->tempBuffer,
2508
                                             stream->output->hpiFormat.wChannels );
2509
    }
2510

    
2511
error:
2512
    return result;
2513
}
2514

    
2515

    
2516
/** Flush output buffers to HPI output stream.
2517
 This completes the processing cycle by writing the temp buffer to the HPI interface.
2518
 Additional output underflows are caught before data is written to the stream, as this
2519
 action typically remedies the underflow and hides it in the process.
2520

2521
 @param stream Pointer to stream struct
2522

2523
 @param numFrames The number of frames to write to the output stream
2524

2525
 @param cbFlags Indicates overflows and underflows
2526
 */
2527
static PaError PaAsiHpi_EndProcessing( PaAsiHpiStream *stream, unsigned long numFrames,
2528
                                       PaStreamCallbackFlags *cbFlags )
2529
{
2530
    PaError result = paNoError;
2531

    
2532
    assert( stream );
2533

    
2534
    if( stream->output )
2535
    {
2536
        PaAsiHpiStreamInfo info;
2537
        /* Check for underflows after the (potentially time-consuming) callback */
2538
        PA_ENSURE_( PaAsiHpi_GetStreamInfo( stream->output, &info ) );
2539
        if( info.underflow )
2540
        {
2541
            *cbFlags |= paOutputUnderflow;
2542
        }
2543

    
2544
        /* Write temp buffer to HPI stream */
2545
        PA_ASIHPI_UNLESS_( HPI_OutStreamWriteBuf( NULL,
2546
                                           stream->output->hpiStream,
2547
                                           stream->output->tempBuffer,
2548
                                           numFrames * stream->output->bytesPerFrame,
2549
                                           &stream->output->hpiFormat),
2550
                           paUnanticipatedHostError );
2551
    }
2552

    
2553
error:
2554
    return result;
2555
}
2556

    
2557

    
2558
/** Main callback engine.
2559
 This function runs in a separate thread and does all the work of fetching audio data from
2560
 the AudioScience card via the HPI interface, feeding it to the user callback via the buffer
2561
 processor, and delivering the resulting output data back to the card via HPI calls.
2562
 It is started and terminated when the PortAudio stream is started and stopped, and starts
2563
 the HPI streams on startup.
2564

2565
 @param userData A pointer to an open stream previously created with Pa_OpenStream.
2566
*/
2567
static void *CallbackThreadFunc( void *userData )
2568
{
2569
    PaError result = paNoError;
2570
    PaAsiHpiStream *stream = (PaAsiHpiStream *) userData;
2571
    int callbackResult = paContinue;
2572

    
2573
    assert( stream );
2574

    
2575
    /* Cleanup routine stops streams on thread exit */
2576
    pthread_cleanup_push( &PaAsiHpi_OnThreadExit, stream );
2577

    
2578
    /* Start HPI streams and notify parent when we're done */
2579
    PA_ENSURE_( PaUnixThread_PrepareNotify( &stream->thread ) );
2580
    /* Buffer will be primed with silence */
2581
    PA_ENSURE_( PaAsiHpi_StartStream( stream, 0 ) );
2582
    PA_ENSURE_( PaUnixThread_NotifyParent( &stream->thread ) );
2583

    
2584
    /* MAIN LOOP */
2585
    while( 1 )
2586
    {
2587
        PaStreamCallbackFlags cbFlags = 0;
2588
        unsigned long framesAvail, framesGot;
2589

    
2590
        pthread_testcancel();
2591

    
2592
        /** @concern StreamStop if the main thread has requested a stop and the stream has not
2593
        * been effectively stopped we signal this condition by modifying callbackResult
2594
        * (we'll want to flush buffered output). */
2595
        if( PaUnixThread_StopRequested( &stream->thread ) && (callbackResult == paContinue) )
2596
        {
2597
            PA_DEBUG(( "Setting callbackResult to paComplete\n" ));
2598
            callbackResult = paComplete;
2599
        }
2600

    
2601
        /* Start winding down thread if requested */
2602
        if( callbackResult != paContinue )
2603
        {
2604
            stream->callbackAbort = (callbackResult == paAbort);
2605
            if( stream->callbackAbort ||
2606
                    /** @concern BlockAdaption: Go on if adaption buffers are empty */
2607
                    PaUtil_IsBufferProcessorOutputEmpty( &stream->bufferProcessor ) )
2608
            {
2609
                goto end;
2610
            }
2611
            PA_DEBUG(( "%s: Flushing buffer processor\n", __FUNCTION__ ));
2612
            /* There is still buffered output that needs to be processed */
2613
        }
2614

    
2615
        /* SLEEP */
2616
        /* Wait for data (or buffer space) to become available. This basically sleeps and
2617
        polls the HPI interface until a full block of frames can be moved. */
2618
        PA_ENSURE_( PaAsiHpi_WaitForFrames( stream, &framesAvail, &cbFlags ) );
2619

    
2620
        /* Consume buffer space. Once we have a number of frames available for consumption we
2621
        must retrieve the data from the HPI interface and pass it to the PA buffer processor.
2622
        We should be prepared to process several chunks successively. */
2623
        while( framesAvail > 0 )
2624
        {
2625
            PaStreamCallbackTimeInfo timeInfo = {0, 0, 0};
2626

    
2627
            pthread_testcancel();
2628

    
2629
            framesGot = framesAvail;
2630
            if( stream->bufferProcessor.hostBufferSizeMode == paUtilFixedHostBufferSize )
2631
            {
2632
                /* We've committed to a fixed host buffer size, stick to that */
2633
                framesGot = framesGot >= stream->maxFramesPerHostBuffer ? stream->maxFramesPerHostBuffer : 0;
2634
            }
2635
            else
2636
            {
2637
                /* We've committed to an upper bound on the size of host buffers */
2638
                assert( stream->bufferProcessor.hostBufferSizeMode == paUtilBoundedHostBufferSize );
2639
                framesGot = PA_MIN( framesGot, stream->maxFramesPerHostBuffer );
2640
            }
2641

    
2642
            /* Obtain buffer timestamps */
2643
            PaAsiHpi_CalculateTimeInfo( stream, &timeInfo );
2644
            PaUtil_BeginBufferProcessing( &stream->bufferProcessor, &timeInfo, cbFlags );
2645
            /* CPU load measurement should include processing activivity external to the stream callback */
2646
            PaUtil_BeginCpuLoadMeasurement( &stream->cpuLoadMeasurer );
2647
            if( framesGot > 0 )
2648
            {
2649
                /* READ FROM HPI INPUT STREAM */
2650
                PA_ENSURE_( PaAsiHpi_BeginProcessing( stream, &framesGot, &cbFlags ) );
2651
                /* Input overflow in a full-duplex stream makes for interesting times */
2652
                if( stream->input && stream->output && (cbFlags & paInputOverflow) )
2653
                {
2654
                    /* Special full-duplex paNeverDropInput mode */
2655
                    if( stream->neverDropInput )
2656
                    {
2657
                        PaUtil_SetNoOutput( &stream->bufferProcessor );
2658
                        cbFlags |= paOutputOverflow;
2659
                    }
2660
                }
2661
                /* CALL USER CALLBACK WITH INPUT DATA, AND OBTAIN OUTPUT DATA */
2662
                PaUtil_EndBufferProcessing( &stream->bufferProcessor, &callbackResult );
2663
                /* Clear overflow and underflow information (but PaAsiHpi_EndProcessing might
2664
                still show up output underflow that will carry over to next round) */
2665
                cbFlags = 0;
2666
                /*  WRITE TO HPI OUTPUT STREAM */
2667
                PA_ENSURE_( PaAsiHpi_EndProcessing( stream, framesGot, &cbFlags ) );
2668
                /* Advance frame counter */
2669
                framesAvail -= framesGot;
2670
            }
2671
            PaUtil_EndCpuLoadMeasurement( &stream->cpuLoadMeasurer, framesGot );
2672

    
2673
            if( framesGot == 0 )
2674
            {
2675
                /* Go back to polling for more frames */
2676
                break;
2677

    
2678
            }
2679
            if( callbackResult != paContinue )
2680
                break;
2681
        }
2682
    }
2683

    
2684
    /* This code is unreachable, but important to include regardless because it
2685
     * is possibly a macro with a closing brace to match the opening brace in
2686
     * pthread_cleanup_push() above.  The documentation states that they must
2687
     * always occur in pairs. */
2688
    pthread_cleanup_pop( 1 );
2689

    
2690
end:
2691
    /* Indicates normal exit of callback, as opposed to the thread getting killed explicitly */
2692
    stream->callbackFinished = 1;
2693
    PA_DEBUG(( "%s: Thread %d exiting (callbackResult = %d)\n ",
2694
               __FUNCTION__, pthread_self(), callbackResult ));
2695
    /* Exit from thread and report any PortAudio error in the process */
2696
    PaUnixThreading_EXIT( result );
2697
error:
2698
    goto end;
2699
}
2700

    
2701
/* --------------------------- Blocking Interface --------------------------- */
2702

    
2703
/* As separate stream interfaces are used for blocking and callback streams, the following
2704
 functions can be guaranteed to only be called for blocking streams. */
2705

    
2706
/** Read data from input stream.
2707
 This reads the indicated number of frames into the supplied buffer from an input stream,
2708
 and blocks until this is done.
2709

2710
 @param s Pointer to PortAudio stream
2711

2712
 @param buffer Pointer to buffer that will receive interleaved data (or an array of pointers
2713
               to a buffer for each non-interleaved channel)
2714

2715
 @param frames Number of frames to read from stream
2716

2717
 @return PortAudio error code (also indicates overflow via paInputOverflowed)
2718
 */
2719
static PaError ReadStream( PaStream *s,
2720
                           void *buffer,
2721
                           unsigned long frames )
2722
{
2723
    PaError result = paNoError;
2724
    PaAsiHpiStream *stream = (PaAsiHpiStream*)s;
2725
    PaAsiHpiStreamInfo info;
2726
    void *userBuffer;
2727

    
2728
    assert( stream );
2729
    PA_UNLESS_( stream->input, paCanNotReadFromAnOutputOnlyStream );
2730

    
2731
    /* Check for input overflow since previous call to ReadStream */
2732
    PA_ENSURE_( PaAsiHpi_GetStreamInfo( stream->input, &info ) );
2733
    if( info.overflow )
2734
    {
2735
        result = paInputOverflowed;
2736
    }
2737

    
2738
    /* NB Make copy of user buffer pointers, since they are advanced by buffer processor */
2739
    if( stream->bufferProcessor.userInputIsInterleaved )
2740
    {
2741
        userBuffer = buffer;
2742
    }
2743
    else
2744
    {
2745
        /* Copy channels into local array */
2746
        userBuffer = stream->blockingUserBufferCopy;
2747
        memcpy( userBuffer, buffer, sizeof (void *) * stream->input->hpiFormat.wChannels );
2748
    }
2749

    
2750
    while( frames > 0 )
2751
    {
2752
        unsigned long framesGot, framesAvail;
2753
        PaStreamCallbackFlags cbFlags = 0;
2754

    
2755
        PA_ENSURE_( PaAsiHpi_WaitForFrames( stream, &framesAvail, &cbFlags ) );
2756
        framesGot = PA_MIN( framesAvail, frames );
2757
        PA_ENSURE_( PaAsiHpi_BeginProcessing( stream, &framesGot, &cbFlags ) );
2758

    
2759
        if( framesGot > 0 )
2760
        {
2761
            framesGot = PaUtil_CopyInput( &stream->bufferProcessor, &userBuffer, framesGot );
2762
            PA_ENSURE_( PaAsiHpi_EndProcessing( stream, framesGot, &cbFlags ) );
2763
            /* Advance frame counter */
2764
            frames -= framesGot;
2765
        }
2766
    }
2767

    
2768
error:
2769
    return result;
2770
}
2771

    
2772

    
2773
/** Write data to output stream.
2774
 This writes the indicated number of frames from the supplied buffer to an output stream,
2775
 and blocks until this is done.
2776

2777
 @param s Pointer to PortAudio stream
2778

2779
 @param buffer Pointer to buffer that provides interleaved data (or an array of pointers
2780
               to a buffer for each non-interleaved channel)
2781

2782
 @param frames Number of frames to write to stream
2783

2784
 @return PortAudio error code (also indicates underflow via paOutputUnderflowed)
2785
 */
2786
static PaError WriteStream( PaStream *s,
2787
                            const void *buffer,
2788
                            unsigned long frames )
2789
{
2790
    PaError result = paNoError;
2791
    PaAsiHpiStream *stream = (PaAsiHpiStream*)s;
2792
    PaAsiHpiStreamInfo info;
2793
    const void *userBuffer;
2794

    
2795
    assert( stream );
2796
    PA_UNLESS_( stream->output, paCanNotWriteToAnInputOnlyStream );
2797

    
2798
    /* Check for output underflow since previous call to WriteStream */
2799
    PA_ENSURE_( PaAsiHpi_GetStreamInfo( stream->output, &info ) );
2800
    if( info.underflow )
2801
    {
2802
        result = paOutputUnderflowed;
2803
    }
2804

    
2805
    /* NB Make copy of user buffer pointers, since they are advanced by buffer processor */
2806
    if( stream->bufferProcessor.userOutputIsInterleaved )
2807
    {
2808
        userBuffer = buffer;
2809
    }
2810
    else
2811
    {
2812
        /* Copy channels into local array */
2813
        userBuffer = stream->blockingUserBufferCopy;
2814
        memcpy( (void *)userBuffer, buffer, sizeof (void *) * stream->output->hpiFormat.wChannels );
2815
    }
2816

    
2817
    while( frames > 0 )
2818
    {
2819
        unsigned long framesGot, framesAvail;
2820
        PaStreamCallbackFlags cbFlags = 0;
2821

    
2822
        PA_ENSURE_( PaAsiHpi_WaitForFrames( stream, &framesAvail, &cbFlags ) );
2823
        framesGot = PA_MIN( framesAvail, frames );
2824
        PA_ENSURE_( PaAsiHpi_BeginProcessing( stream, &framesGot, &cbFlags ) );
2825

    
2826
        if( framesGot > 0 )
2827
        {
2828
            framesGot = PaUtil_CopyOutput( &stream->bufferProcessor, &userBuffer, framesGot );
2829
            PA_ENSURE_( PaAsiHpi_EndProcessing( stream, framesGot, &cbFlags ) );
2830
            /* Advance frame counter */
2831
            frames -= framesGot;
2832
        }
2833
    }
2834

    
2835
error:
2836
    return result;
2837
}
2838

    
2839

    
2840
/** Number of frames that can be read from input stream without blocking.
2841

2842
 @param s Pointer to PortAudio stream
2843

2844
 @return Number of frames, or PortAudio error code
2845
 */
2846
static signed long GetStreamReadAvailable( PaStream *s )
2847
{
2848
    PaError result = paNoError;
2849
    PaAsiHpiStream *stream = (PaAsiHpiStream*)s;
2850
    PaAsiHpiStreamInfo info;
2851

    
2852
    assert( stream );
2853
    PA_UNLESS_( stream->input, paCanNotReadFromAnOutputOnlyStream );
2854

    
2855
    PA_ENSURE_( PaAsiHpi_GetStreamInfo( stream->input, &info ) );
2856
    /* Round down to the nearest host buffer multiple */
2857
    result = (info.availableFrames / stream->maxFramesPerHostBuffer) * stream->maxFramesPerHostBuffer;
2858
    if( info.overflow )
2859
    {
2860
        result = paInputOverflowed;
2861
    }
2862

    
2863
error:
2864
    return result;
2865
}
2866

    
2867

    
2868
/** Number of frames that can be written to output stream without blocking.
2869

2870
 @param s Pointer to PortAudio stream
2871

2872
 @return Number of frames, or PortAudio error code
2873
 */
2874
static signed long GetStreamWriteAvailable( PaStream *s )
2875
{
2876
    PaError result = paNoError;
2877
    PaAsiHpiStream *stream = (PaAsiHpiStream*)s;
2878
    PaAsiHpiStreamInfo info;
2879

    
2880
    assert( stream );
2881
    PA_UNLESS_( stream->output, paCanNotWriteToAnInputOnlyStream );
2882

    
2883
    PA_ENSURE_( PaAsiHpi_GetStreamInfo( stream->output, &info ) );
2884
    /* Round down to the nearest host buffer multiple */
2885
    result = (info.availableFrames / stream->maxFramesPerHostBuffer) * stream->maxFramesPerHostBuffer;
2886
    if( info.underflow )
2887
    {
2888
        result = paOutputUnderflowed;
2889
    }
2890

    
2891
error:
2892
    return result;
2893
}