~ubuntu-branches/ubuntu/wily/sflphone/wily

« back to all changes in this revision

Viewing changes to daemon/libs/pjproject-2.1.0/third_party/portaudio/src/hostapi/asihpi/pa_linux_asihpi.c

  • Committer: Package Import Robot
  • Author(s): Mark Purcell
  • Date: 2014-01-28 18:23:36 UTC
  • mfrom: (1.1.11)
  • mto: This revision was merged to the branch mainline in revision 24.
  • Revision ID: package-import@ubuntu.com-20140128182336-3xenud1kbnwmf3mz
* New upstream release 
  - Fixes "New Upstream Release" (Closes: #735846)
  - Fixes "Ringtone does not stop" (Closes: #727164)
  - Fixes "[sflphone-kde] crash on startup" (Closes: #718178)
  - Fixes "sflphone GUI crashes when call is hung up" (Closes: #736583)
* Build-Depends: ensure GnuTLS 2.6
  - libucommon-dev (>= 6.0.7-1.1), libccrtp-dev (>= 2.0.6-3)
  - Fixes "FTBFS Build-Depends libgnutls{26,28}-dev" (Closes: #722040)
* Fix "boost 1.49 is going away" unversioned Build-Depends: (Closes: #736746)
* Add Build-Depends: libsndfile-dev, nepomuk-core-dev

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * PortAudio Portable Real-Time Audio Library
 
3
 * Latest Version at: http://www.portaudio.com
 
4
 *
 
5
 * PortAudio v18 version of AudioScience HPI driver by Fred Gleason <fredg@salemradiolabs.com>
 
6
 * PortAudio v19 version of AudioScience HPI driver by Ludwig Schwardt <schwardt@sun.ac.za>
 
7
 *
 
8
 * Copyright (c) 2003 Fred Gleason
 
9
 * Copyright (c) 2005,2006 Ludwig Schwardt
 
10
 *
 
11
 * Permission is hereby granted, free of charge, to any person obtaining
 
12
 * a copy of this software and associated documentation files
 
13
 * (the "Software"), to deal in the Software without restriction,
 
14
 * including without limitation the rights to use, copy, modify, merge,
 
15
 * publish, distribute, sublicense, and/or sell copies of the Software,
 
16
 * and to permit persons to whom the Software is furnished to do so,
 
17
 * subject to the following conditions:
 
18
 *
 
19
 * The above copyright notice and this permission notice shall be
 
20
 * included in all copies or substantial portions of the Software.
 
21
 *
 
22
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 
23
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 
24
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 
25
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
 
26
 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
 
27
 * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 
28
 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 
29
 */
 
30
 
 
31
/*
 
32
 * The text above constitutes the entire PortAudio license; however,
 
33
 * the PortAudio community also makes the following non-binding requests:
 
34
 *
 
35
 * Any person wishing to distribute modifications to the Software is
 
36
 * requested to send the modifications to the original developer so that
 
37
 * they can be incorporated into the canonical version. It is also
 
38
 * requested that these non-binding requests be included along with the
 
39
 * license above.
 
40
 */
 
41
 
 
42
/*
 
43
 * Modification History
 
44
 * 12/2003 - Initial version
 
45
 * 09/2005 - v19 version [rewrite]
 
46
 */
 
47
 
 
48
/** @file
 
49
 @ingroup hostapi_src
 
50
 @brief Host API implementation supporting AudioScience cards
 
51
        via the Linux HPI interface.
 
52
 
 
53
 <h3>Overview</h3>
 
54
 
 
55
 This is a PortAudio implementation for the AudioScience HPI Audio API
 
56
 on the Linux platform. AudioScience makes a range of audio adapters customised
 
57
 for the broadcasting industry, with support for both Windows and Linux.
 
58
 More information on their products can be found on their website:
 
59
 
 
60
     http://www.audioscience.com
 
61
 
 
62
 Documentation for the HPI API can be found at:
 
63
 
 
64
     http://www.audioscience.com/internet/download/sdk/spchpi.pdf
 
65
 
 
66
 The Linux HPI driver itself (a kernel module + library) can be downloaded from:
 
67
 
 
68
     http://www.audioscience.com/internet/download/linux_drivers.htm
 
69
 
 
70
 <h3>Implementation strategy</h3>
 
71
 
 
72
 *Note* Ideally, AudioScience cards should be handled by the PortAudio ALSA
 
73
 implementation on Linux, as ALSA is the preferred Linux soundcard API. The existence
 
74
 of this host API implementation might therefore seem a bit flawed. Unfortunately, at
 
75
 the time of the creation of this implementation (June 2006), the PA ALSA implementation
 
76
 could not make use of the existing AudioScience ALSA driver. PA ALSA uses the
 
77
 "memory-mapped" (mmap) ALSA access mode to interact with the ALSA library, while the
 
78
 AudioScience ALSA driver only supports the "read-write" access mode. The appropriate
 
79
 solution to this problem is to add "read-write" support to PortAudio ALSA, thereby
 
80
 extending the range of soundcards it supports (AudioScience cards are not the only
 
81
 ones with this problem). Given the author's limited knowledge of ALSA and the
 
82
 simplicity of the HPI API, the second-best solution was born...
 
83
 
 
84
 The following mapping between HPI and PA was followed:
 
85
 HPI subsystem => PortAudio host API
 
86
 HPI adapter => nothing specific
 
87
 HPI stream => PortAudio device
 
88
 
 
89
 Each HPI stream is either input or output (not both), and can support
 
90
 different channel counts, sampling rates and sample formats. It is therefore
 
91
 a more natural fit to a PA device. A PA stream can therefore combine two
 
92
 HPI streams (one input and one output) into a "full-duplex" stream. These
 
93
 HPI streams can even be on different physical adapters. The two streams ought to be
 
94
 sample-synchronised when they reside on the same adapter, as most AudioScience adapters
 
95
 derive their ADC and DAC clocks from one master clock. When combining two adapters
 
96
 into one full-duplex stream, however, the use of a word clock connection between the
 
97
 adapters is strongly recommended.
 
98
 
 
99
 The HPI interface is inherently blocking, making use of read and write calls to
 
100
 transfer data between user buffers and driver buffers. The callback interface therefore
 
101
 requires a helper thread ("callback engine") which periodically transfers data (one thread
 
102
 per PA stream, in fact). The current implementation explicitly sleeps via Pa_Sleep() until
 
103
 enough samples can be transferred (select() or poll() would be better, but currently seems
 
104
 impossible...). The thread implementation makes use of the Unix thread helper functions
 
105
 and some pthread calls here and there. If a unified PA thread exists, this host API
 
106
 implementation might also compile on Windows, as this is the only real Linux-specific
 
107
 part of the code.
 
108
 
 
109
 There is no inherent fixed buffer size in the HPI interface, as in some other host APIs.
 
110
 The PortAudio implementation contains a buffer that is allocated during OpenStream and
 
111
 used to transfer data between the callback and the HPI driver buffer. The size of this
 
112
 buffer is quite flexible and is derived from latency suggestions and matched to the
 
113
 requested callback buffer size as far as possible. It can become quite huge, as the
 
114
 AudioScience cards are typically geared towards higher-latency applications and contain
 
115
 large hardware buffers.
 
116
 
 
117
 The HPI interface natively supports most common sample formats and sample rates (some
 
118
 conversion is done on the adapter itself).
 
119
 
 
120
 Stream time is measured based on the number of processed frames, which is adjusted by the
 
121
 number of frames currently buffered by the HPI driver.
 
122
 
 
123
 There is basic support for detecting overflow and underflow. The HPI interface does not
 
124
 explicitly indicate this, so thresholds on buffer levels are used in combination with
 
125
 stream state. Recovery from overflow and underflow is left to the PA client.
 
126
 
 
127
 Blocking streams are also implemented. It makes use of the same polling routines that
 
128
 the callback interface uses, in order to prevent the allocation of variable-sized
 
129
 buffers during reading and writing. The framesPerBuffer parameter is therefore still
 
130
 relevant, and this can be increased in the blocking case to improve efficiency.
 
131
 
 
132
 The implementation contains extensive reporting macros (slightly modified PA_ENSURE and
 
133
 PA_UNLESS versions) and a useful stream dump routine to provide debugging feedback.
 
134
 
 
135
 Output buffer priming via the user callback (i.e. paPrimeOutputBuffersUsingStreamCallback
 
136
 and friends) is not implemented yet. All output is primed with silence.
 
137
 
 
138
 Please send bug reports etc. to Ludwig Schwardt <schwardt@sun.ac.za>
 
139
 */
 
140
 
 
141
#include <unistd.h>
 
142
#include <stdio.h>
 
143
#include <stdlib.h>
 
144
#include <string.h>          /* strlen() */
 
145
#include <pthread.h>         /* pthreads and friends */
 
146
#include <assert.h>          /* assert */
 
147
#include <math.h>            /* ceil, floor */
 
148
 
 
149
#include <asihpi/hpi.h>      /* HPI API */
 
150
 
 
151
#include "portaudio.h"       /* PortAudio API */
 
152
#include "pa_util.h"         /* PA_DEBUG, other small utilities */
 
153
#include "pa_unix_util.h"    /* Unix threading utilities */
 
154
#include "pa_allocation.h"   /* Group memory allocation */
 
155
#include "pa_hostapi.h"      /* Host API structs */
 
156
#include "pa_stream.h"       /* Stream interface structs */
 
157
#include "pa_cpuload.h"      /* CPU load measurer */
 
158
#include "pa_process.h"      /* Buffer processor */
 
159
#include "pa_converters.h"   /* PaUtilZeroer */
 
160
#include "pa_debugprint.h"
 
161
 
 
162
/* -------------------------------------------------------------------------- */
 
163
 
 
164
/*
 
165
 * Defines
 
166
 */
 
167
 
 
168
/* Error reporting and assertions */
 
169
 
 
170
/** Evaluate expression, and return on any PortAudio errors */
 
171
#define PA_ENSURE_(expr) \
 
172
    do { \
 
173
        PaError paError = (expr); \
 
174
        if( UNLIKELY( paError < paNoError ) ) \
 
175
        { \
 
176
            PA_DEBUG(( "Expression '" #expr "' failed in '" __FILE__ "', line: " STRINGIZE( __LINE__ ) "\n" )); \
 
177
            result = paError; \
 
178
            goto error; \
 
179
        } \
 
180
    } while (0);
 
181
 
 
182
/** Assert expression, else return the provided PaError */
 
183
#define PA_UNLESS_(expr, paError) \
 
184
    do { \
 
185
        if( UNLIKELY( (expr) == 0 ) ) \
 
186
        { \
 
187
            PA_DEBUG(( "Expression '" #expr "' failed in '" __FILE__ "', line: " STRINGIZE( __LINE__ ) "\n" )); \
 
188
            result = (paError); \
 
189
            goto error; \
 
190
        } \
 
191
    } while( 0 );
 
192
 
 
193
/** Check return value of HPI function, and map it to PaError */
 
194
#define PA_ASIHPI_UNLESS_(expr, paError) \
 
195
    do { \
 
196
        HW16 hpiError = (expr); \
 
197
        /* If HPI error occurred */ \
 
198
        if( UNLIKELY( hpiError ) ) \
 
199
        { \
 
200
            char szError[256]; \
 
201
            HPI_GetErrorText( hpiError, szError ); \
 
202
            PA_DEBUG(( "HPI error %d occurred: %s\n", hpiError, szError )); \
 
203
            /* This message will always be displayed, even if debug info is disabled */ \
 
204
            PA_DEBUG(( "Expression '" #expr "' failed in '" __FILE__ "', line: " STRINGIZE( __LINE__ ) "\n" )); \
 
205
            if( (paError) == paUnanticipatedHostError ) \
 
206
            { \
 
207
                PA_DEBUG(( "Host error description: %s\n", szError )); \
 
208
                /* PaUtil_SetLastHostErrorInfo should only be used in the main thread */ \
 
209
                if( pthread_equal( pthread_self(), paUnixMainThread ) ) \
 
210
                { \
 
211
                    PaUtil_SetLastHostErrorInfo( paInDevelopment, hpiError, szError ); \
 
212
                } \
 
213
            } \
 
214
            /* If paNoError is specified, continue as usual */ \
 
215
            /* (useful if you only want to print out the debug messages above) */ \
 
216
            if( (paError) < 0 ) \
 
217
            { \
 
218
                result = (paError); \
 
219
                goto error; \
 
220
            } \
 
221
        } \
 
222
    } while( 0 );
 
223
 
 
224
/** Report HPI error code and text */
 
225
#define PA_ASIHPI_REPORT_ERROR_(hpiErrorCode) \
 
226
    do { \
 
227
        char szError[256]; \
 
228
        HPI_GetErrorText( hpiError, szError ); \
 
229
        PA_DEBUG(( "HPI error %d occurred: %s\n", hpiError, szError )); \
 
230
        /* PaUtil_SetLastHostErrorInfo should only be used in the main thread */ \
 
231
        if( pthread_equal( pthread_self(), paUnixMainThread ) ) \
 
232
        { \
 
233
            PaUtil_SetLastHostErrorInfo( paInDevelopment, (hpiErrorCode), szError ); \
 
234
        } \
 
235
    } while( 0 );
 
236
 
 
237
/* Defaults */
 
238
 
 
239
/** Sample formats available natively on AudioScience hardware */
 
240
#define PA_ASIHPI_AVAILABLE_FORMATS_ (paFloat32 | paInt32 | paInt24 | paInt16 | paUInt8)
 
241
/** Enable background bus mastering (BBM) for buffer transfers, if available (see HPI docs) */
 
242
#define PA_ASIHPI_USE_BBM_ 1
 
243
/** Minimum number of frames in HPI buffer (for either data or available space).
 
244
 If buffer contains less data/space, it indicates xrun or completion. */
 
245
#define PA_ASIHPI_MIN_FRAMES_ 1152
 
246
/** Minimum polling interval in milliseconds, which determines minimum host buffer size */
 
247
#define PA_ASIHPI_MIN_POLLING_INTERVAL_ 10
 
248
 
 
249
/* -------------------------------------------------------------------------- */
 
250
 
 
251
/*
 
252
 * Structures
 
253
 */
 
254
 
 
255
/** Host API global data */
 
256
typedef struct PaAsiHpiHostApiRepresentation
 
257
{
 
258
    /* PortAudio "base class" - keep the baseRep first! (C-style inheritance) */
 
259
    PaUtilHostApiRepresentation baseHostApiRep;
 
260
    PaUtilStreamInterface callbackStreamInterface;
 
261
    PaUtilStreamInterface blockingStreamInterface;
 
262
 
 
263
    PaUtilAllocationGroup *allocations;
 
264
 
 
265
    /* implementation specific data goes here */
 
266
 
 
267
    PaHostApiIndex hostApiIndex;
 
268
    /** HPI subsystem pointer */
 
269
    HPI_HSUBSYS *subSys;
 
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
    /** HPI subsystem (required for most HPI calls) */
 
284
    HPI_HSUBSYS *subSys;
 
285
    /** Adapter index */
 
286
    HW16 adapterIndex;
 
287
    /** Adapter model number (hex) */
 
288
    HW16 adapterType;
 
289
    /** Adapter HW/SW version */
 
290
    HW16 adapterVersion;
 
291
    /** Adapter serial number */
 
292
    HW32 adapterSerialNumber;
 
293
    /** Stream number */
 
294
    HW16 streamIndex;
 
295
    /** 0=Input, 1=Output (HPI streams are either input or output but not both) */
 
296
    HW16 streamIsOutput;
 
297
}
 
298
PaAsiHpiDeviceInfo;
 
299
 
 
300
 
 
301
/** Stream state as defined by PortAudio.
 
302
 It seems that the host API implementation has to keep track of the PortAudio stream state.
 
303
 Please note that this is NOT the same as the state of the underlying HPI stream. By separating
 
304
 these two concepts, a lot of flexibility is gained. There is a rough match between the two,
 
305
 of course, but forcing a precise match is difficult. For example, HPI_STATE_DRAINED can occur
 
306
 during the Active state of PortAudio (due to underruns) and also during CallBackFinished in
 
307
 the case of an output stream. Similarly, HPI_STATE_STOPPED mostly coincides with the Stopped
 
308
 PortAudio state, by may also occur in the CallbackFinished state when recording is finished.
 
309
 
 
310
 Here is a rough match-up:
 
311
 
 
312
 PortAudio state   =>     HPI state
 
313
 ---------------          ---------
 
314
 Active            =>     HPI_STATE_RECORDING, HPI_STATE_PLAYING, (HPI_STATE_DRAINED)
 
315
 Stopped           =>     HPI_STATE_STOPPED
 
316
 CallbackFinished  =>     HPI_STATE_STOPPED, HPI_STATE_DRAINED */
 
317
typedef enum PaAsiHpiStreamState
 
318
{
 
319
    paAsiHpiStoppedState=0,
 
320
    paAsiHpiActiveState=1,
 
321
    paAsiHpiCallbackFinishedState=2
 
322
}
 
323
PaAsiHpiStreamState;
 
324
 
 
325
 
 
326
/** Stream component data (associated with one direction, i.e. either input or output) */
 
327
typedef struct PaAsiHpiStreamComponent
 
328
{
 
329
    /** Device information (HPI handles, etc) */
 
330
    PaAsiHpiDeviceInfo *hpiDevice;
 
331
    /** Stream handle, as passed to HPI interface.
 
332
     HACK: we assume types HPI_HISTREAM and HPI_HOSTREAM are the same...
 
333
     (both are HW32 up to version 3.00 of ASIHPI, and hopefully they stay that way) */
 
334
    HPI_HISTREAM hpiStream;
 
335
    /** Stream format, as passed to HPI interface */
 
336
    HPI_FORMAT hpiFormat;
 
337
    /** Number of bytes per frame, derived from hpiFormat and saved for convenience */
 
338
    HW32 bytesPerFrame;
 
339
    /** Size of hardware (on-card) buffer of stream in bytes */
 
340
    HW32 hardwareBufferSize;
 
341
    /** Size of host (BBM) buffer of stream in bytes (if used) */
 
342
    HW32 hostBufferSize;
 
343
    /** Upper limit on the utilization of output stream buffer (both hardware and host).
 
344
     This prevents large latencies in an output-only stream with a potentially huge buffer
 
345
     and a fast data generator, which would otherwise keep the hardware buffer filled to
 
346
     capacity. See also the "Hardware Buffering=off" option in the AudioScience WAV driver. */
 
347
    HW32 outputBufferCap;
 
348
    /** Sample buffer (halfway station between HPI and buffer processor) */
 
349
    HW8 *tempBuffer;
 
350
    /** Sample buffer size, in bytes */
 
351
    HW32 tempBufferSize;
 
352
}
 
353
PaAsiHpiStreamComponent;
 
354
 
 
355
 
 
356
/** Stream data */
 
357
typedef struct PaAsiHpiStream
 
358
{
 
359
    /* PortAudio "base class" - keep the baseRep first! (C-style inheritance) */
 
360
    PaUtilStreamRepresentation baseStreamRep;
 
361
    PaUtilCpuLoadMeasurer cpuLoadMeasurer;
 
362
    PaUtilBufferProcessor bufferProcessor;
 
363
 
 
364
    PaUtilAllocationGroup *allocations;
 
365
 
 
366
    /* implementation specific data goes here */
 
367
 
 
368
    /** Separate structs for input and output sides of stream */
 
369
    PaAsiHpiStreamComponent *input, *output;
 
370
 
 
371
    /** Polling interval (in milliseconds) */
 
372
    HW32 pollingInterval;
 
373
    /** Are we running in callback mode? */
 
374
    int callbackMode;
 
375
    /** Number of frames to transfer at a time to/from HPI */
 
376
    unsigned long maxFramesPerHostBuffer;
 
377
    /** Indicates that the stream is in the paNeverDropInput mode */
 
378
    int neverDropInput;
 
379
    /** Contains copy of user buffers, used by blocking interface to transfer non-interleaved data.
 
380
     It went here instead of to each stream component, as the stream component buffer setup in
 
381
     PaAsiHpi_SetupBuffers doesn't know the stream details such as callbackMode.
 
382
     (Maybe a problem later if ReadStream and WriteStream happens concurrently on same stream.) */
 
383
    void **blockingUserBufferCopy;
 
384
 
 
385
    /* Thread-related variables */
 
386
 
 
387
    /** Helper thread which will deliver data to user callback */
 
388
    PaUnixThread thread;
 
389
    /** PortAudio stream state (Active/Stopped/CallbackFinished) */
 
390
    volatile sig_atomic_t state;
 
391
    /** Hard abort, i.e. drop frames? */
 
392
    volatile sig_atomic_t callbackAbort;
 
393
    /** True if stream stopped via exiting callback with paComplete/paAbort flag
 
394
     (as opposed to explicit call to StopStream/AbortStream) */
 
395
    volatile sig_atomic_t callbackFinished;
 
396
}
 
397
PaAsiHpiStream;
 
398
 
 
399
 
 
400
/** Stream state information, collected together for convenience */
 
401
typedef struct PaAsiHpiStreamInfo
 
402
{
 
403
    /** HPI stream state (HPI_STATE_STOPPED, HPI_STATE_PLAYING, etc.) */
 
404
    HW16 state;
 
405
    /** Size (in bytes) of recording/playback data buffer in HPI driver */
 
406
    HW32 bufferSize;
 
407
    /** Amount of data (in bytes) available in the buffer */
 
408
    HW32 dataSize;
 
409
    /** Number of frames played/recorded since last stream reset */
 
410
    HW32 frameCounter;
 
411
    /** Amount of data (in bytes) in hardware (on-card) buffer.
 
412
     This differs from dataSize if bus mastering (BBM) is used, which introduces another
 
413
     driver-level buffer to which dataSize/bufferSize then refers. */
 
414
    HW32 auxDataSize;
 
415
    /** Total number of data frames currently buffered by HPI driver (host + hw buffers) */
 
416
    HW32 totalBufferedData;
 
417
    /** Size of immediately available data (for input) or space (for output) in frames.
 
418
     This only checks the first-level buffer (typically host buffer). This amount can be
 
419
     transferred immediately. */
 
420
    HW32 availableFrames;
 
421
    /** Indicates that hardware buffer is getting too full */
 
422
    int overflow;
 
423
    /** Indicates that hardware buffer is getting too empty */
 
424
    int underflow;
 
425
}
 
426
PaAsiHpiStreamInfo;
 
427
 
 
428
/* -------------------------------------------------------------------------- */
 
429
 
 
430
/*
 
431
 * Function prototypes
 
432
 */
 
433
 
 
434
#ifdef __cplusplus
 
435
extern "C"
 
436
{
 
437
#endif /* __cplusplus */
 
438
 
 
439
    /* The only exposed function in the entire host API implementation */
 
440
    PaError PaAsiHpi_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
 
441
 
 
442
#ifdef __cplusplus
 
443
}
 
444
#endif /* __cplusplus */
 
445
 
 
446
static void Terminate( struct PaUtilHostApiRepresentation *hostApi );
 
447
static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
 
448
                                  const PaStreamParameters *inputParameters,
 
449
                                  const PaStreamParameters *outputParameters,
 
450
                                  double sampleRate );
 
451
 
 
452
/* Stream prototypes */
 
453
static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
 
454
                           PaStream **s,
 
455
                           const PaStreamParameters *inputParameters,
 
456
                           const PaStreamParameters *outputParameters,
 
457
                           double sampleRate,
 
458
                           unsigned long framesPerBuffer,
 
459
                           PaStreamFlags streamFlags,
 
460
                           PaStreamCallback *streamCallback,
 
461
                           void *userData );
 
462
static PaError CloseStream( PaStream *s );
 
463
static PaError StartStream( PaStream *s );
 
464
static PaError StopStream( PaStream *s );
 
465
static PaError AbortStream( PaStream *s );
 
466
static PaError IsStreamStopped( PaStream *s );
 
467
static PaError IsStreamActive( PaStream *s );
 
468
static PaTime GetStreamTime( PaStream *s );
 
469
static double GetStreamCpuLoad( PaStream *s );
 
470
 
 
471
/* Blocking prototypes */
 
472
static PaError ReadStream( PaStream *s, void *buffer, unsigned long frames );
 
473
static PaError WriteStream( PaStream *s, const void *buffer, unsigned long frames );
 
474
static signed long GetStreamReadAvailable( PaStream *s );
 
475
static signed long GetStreamWriteAvailable( PaStream *s );
 
476
 
 
477
/* Callback prototypes */
 
478
static void *CallbackThreadFunc( void *userData );
 
479
 
 
480
/* Functions specific to this API */
 
481
static PaError PaAsiHpi_BuildDeviceList( PaAsiHpiHostApiRepresentation *hpiHostApi );
 
482
static HW16 PaAsiHpi_PaToHpiFormat( PaSampleFormat paFormat );
 
483
static PaSampleFormat PaAsiHpi_HpiToPaFormat( HW16 hpiFormat );
 
484
static PaError PaAsiHpi_CreateFormat( struct PaUtilHostApiRepresentation *hostApi,
 
485
                                      const PaStreamParameters *parameters, double sampleRate,
 
486
                                      PaAsiHpiDeviceInfo **hpiDevice, HPI_FORMAT *hpiFormat );
 
487
static PaError PaAsiHpi_OpenInput( struct PaUtilHostApiRepresentation *hostApi,
 
488
                                   const PaAsiHpiDeviceInfo *hpiDevice, const HPI_FORMAT *hpiFormat,
 
489
                                   HPI_HISTREAM *hpiStream );
 
490
static PaError PaAsiHpi_OpenOutput( struct PaUtilHostApiRepresentation *hostApi,
 
491
                                    const PaAsiHpiDeviceInfo *hpiDevice, const HPI_FORMAT *hpiFormat,
 
492
                                    HPI_HOSTREAM *hpiStream );
 
493
static PaError PaAsiHpi_GetStreamInfo( PaAsiHpiStreamComponent *streamComp, PaAsiHpiStreamInfo *info );
 
494
static void PaAsiHpi_StreamComponentDump( PaAsiHpiStreamComponent *streamComp, PaAsiHpiStream *stream );
 
495
static void PaAsiHpi_StreamDump( PaAsiHpiStream *stream );
 
496
static PaError PaAsiHpi_SetupBuffers( PaAsiHpiStreamComponent *streamComp, HW32 pollingInterval,
 
497
                                      unsigned long framesPerPaHostBuffer, PaTime suggestedLatency );
 
498
static PaError PaAsiHpi_PrimeOutputWithSilence( PaAsiHpiStream *stream );
 
499
static PaError PaAsiHpi_StartStream( PaAsiHpiStream *stream, int outputPrimed );
 
500
static PaError PaAsiHpi_StopStream( PaAsiHpiStream *stream, int abort );
 
501
static PaError PaAsiHpi_ExplicitStop( PaAsiHpiStream *stream, int abort );
 
502
static void PaAsiHpi_OnThreadExit( void *userData );
 
503
static PaError PaAsiHpi_WaitForFrames( PaAsiHpiStream *stream, unsigned long *framesAvail,
 
504
                                       PaStreamCallbackFlags *cbFlags );
 
505
static void PaAsiHpi_CalculateTimeInfo( PaAsiHpiStream *stream, PaStreamCallbackTimeInfo *timeInfo );
 
506
static PaError PaAsiHpi_BeginProcessing( PaAsiHpiStream* stream, unsigned long* numFrames,
 
507
        PaStreamCallbackFlags *cbFlags );
 
508
static PaError PaAsiHpi_EndProcessing( PaAsiHpiStream *stream, unsigned long numFrames,
 
509
                                       PaStreamCallbackFlags *cbFlags );
 
510
 
 
511
/* ==========================================================================
 
512
 * ============================= IMPLEMENTATION =============================
 
513
 * ========================================================================== */
 
514
 
 
515
/* --------------------------- Host API Interface --------------------------- */
 
516
 
 
517
/** Enumerate all PA devices (= HPI streams).
 
518
 This compiles a list of all HPI adapters, and registers a PA device for each input and
 
519
 output stream it finds. Most errors are ignored, as missing or erroneous devices are
 
520
 simply skipped.
 
521
 
 
522
 @param hpiHostApi Pointer to HPI host API struct
 
523
 
 
524
 @return PortAudio error code (only paInsufficientMemory in practice)
 
525
 */
 
526
static PaError PaAsiHpi_BuildDeviceList( PaAsiHpiHostApiRepresentation *hpiHostApi )
 
527
{
 
528
    PaError result = paNoError;
 
529
    PaUtilHostApiRepresentation *hostApi = &hpiHostApi->baseHostApiRep;
 
530
    PaHostApiInfo *baseApiInfo = &hostApi->info;
 
531
    PaAsiHpiDeviceInfo *hpiDeviceList;
 
532
    HW16 adapterList[ HPI_MAX_ADAPTERS ];
 
533
    HW16 numAdapters;
 
534
    HW16 hpiError = 0;
 
535
    int i, j, deviceCount = 0, deviceIndex = 0;
 
536
 
 
537
    assert( hpiHostApi );
 
538
    assert( hpiHostApi->subSys );
 
539
 
 
540
    /* Look for adapters (not strictly necessary, as AdapterOpen can do the same, but this */
 
541
    /* way we have less errors since we do not try to open adapters we know aren't there) */
 
542
    /* Errors not considered critical here (subsystem may report 0 devices), but report them */
 
543
    /* in debug mode. */
 
544
    PA_ASIHPI_UNLESS_( HPI_SubSysFindAdapters( hpiHostApi->subSys, &numAdapters,
 
545
                       adapterList, HPI_MAX_ADAPTERS ), paNoError );
 
546
 
 
547
    /* First open and count the number of devices (= number of streams), to ease memory allocation */
 
548
    for( i=0; i < HPI_MAX_ADAPTERS; ++i )
 
549
    {
 
550
        HW16 inStreams, outStreams;
 
551
        HW16 version;
 
552
        HW32 serial;
 
553
        HW16 type;
 
554
 
 
555
        /* If no adapter found at this index, skip it */
 
556
        if( adapterList[i] == 0 )
 
557
            continue;
 
558
 
 
559
        /* Try to open adapter */
 
560
        hpiError = HPI_AdapterOpen( hpiHostApi->subSys, i );
 
561
        /* Report error and skip to next device on failure */
 
562
        if( hpiError )
 
563
        {
 
564
            PA_ASIHPI_REPORT_ERROR_( hpiError );
 
565
            continue;
 
566
        }
 
567
        hpiError = HPI_AdapterGetInfo( hpiHostApi->subSys, i,
 
568
                                       &outStreams, &inStreams, &version, &serial, &type );
 
569
        /* Skip to next device on failure */
 
570
        if( hpiError )
 
571
        {
 
572
            PA_ASIHPI_REPORT_ERROR_( hpiError );
 
573
            continue;
 
574
        }
 
575
        else
 
576
        {
 
577
            /* Assign default devices if available and increment device count */
 
578
            if( (baseApiInfo->defaultInputDevice == paNoDevice) && (inStreams > 0) )
 
579
                baseApiInfo->defaultInputDevice = deviceCount;
 
580
            deviceCount += inStreams;
 
581
            if( (baseApiInfo->defaultOutputDevice == paNoDevice) && (outStreams > 0) )
 
582
                baseApiInfo->defaultOutputDevice = deviceCount;
 
583
            deviceCount += outStreams;
 
584
        }
 
585
    }
 
586
 
 
587
    /* Register any discovered devices */
 
588
    if( deviceCount > 0 )
 
589
    {
 
590
        /* Memory allocation */
 
591
        PA_UNLESS_( hostApi->deviceInfos = (PaDeviceInfo**) PaUtil_GroupAllocateMemory(
 
592
                                               hpiHostApi->allocations, sizeof(PaDeviceInfo*) * deviceCount ),
 
593
                    paInsufficientMemory );
 
594
        /* Allocate all device info structs in a contiguous block */
 
595
        PA_UNLESS_( hpiDeviceList = (PaAsiHpiDeviceInfo*) PaUtil_GroupAllocateMemory(
 
596
                                        hpiHostApi->allocations, sizeof(PaAsiHpiDeviceInfo) * deviceCount ),
 
597
                    paInsufficientMemory );
 
598
 
 
599
        /* Now query devices again for information */
 
600
        for( i=0; i < HPI_MAX_ADAPTERS; ++i )
 
601
        {
 
602
            HW16 inStreams, outStreams;
 
603
            HW16 version;
 
604
            HW32 serial;
 
605
            HW16 type;
 
606
 
 
607
            /* If no adapter found at this index, skip it */
 
608
            if( adapterList[i] == 0 )
 
609
                continue;
 
610
 
 
611
            /* Assume adapter is still open from previous round */
 
612
            hpiError = HPI_AdapterGetInfo( hpiHostApi->subSys, i,
 
613
                                           &outStreams, &inStreams, &version, &serial, &type );
 
614
            /* Report error and skip to next device on failure */
 
615
            if( hpiError )
 
616
            {
 
617
                PA_ASIHPI_REPORT_ERROR_( hpiError );
 
618
                continue;
 
619
            }
 
620
            else
 
621
            {
 
622
                PA_DEBUG(( "Found HPI Adapter ID=%4X Idx=%d #In=%d #Out=%d S/N=%d HWver=%c%d DSPver=%03d\n",
 
623
                           type, i, inStreams, outStreams, serial,
 
624
                           ((version>>3)&0xf)+'A',                  /* Hw version major */
 
625
                           version&0x7,                             /* Hw version minor */
 
626
                           ((version>>13)*100)+((version>>7)&0x3f)  /* DSP code version */
 
627
                         ));
 
628
            }
 
629
 
 
630
            /* First add all input streams as devices */
 
631
            for( j=0; j < inStreams; ++j )
 
632
            {
 
633
                PaAsiHpiDeviceInfo *hpiDevice = &hpiDeviceList[deviceIndex];
 
634
                PaDeviceInfo *baseDeviceInfo = &hpiDevice->baseDeviceInfo;
 
635
                char srcName[72];
 
636
                char *deviceName;
 
637
 
 
638
                memset( hpiDevice, 0, sizeof(PaAsiHpiDeviceInfo) );
 
639
                /* Set implementation-specific device details */
 
640
                hpiDevice->subSys = hpiHostApi->subSys;
 
641
                hpiDevice->adapterIndex = i;
 
642
                hpiDevice->adapterType = type;
 
643
                hpiDevice->adapterVersion = version;
 
644
                hpiDevice->adapterSerialNumber = serial;
 
645
                hpiDevice->streamIndex = j;
 
646
                hpiDevice->streamIsOutput = 0;
 
647
                /* Set common PortAudio device stats */
 
648
                baseDeviceInfo->structVersion = 2;
 
649
                /* Make sure name string is owned by API info structure */
 
650
                sprintf( srcName,
 
651
                         "Adapter %d (%4X) - Input Stream %d", i+1, type, j+1 );
 
652
                PA_UNLESS_( deviceName = (char *) PaUtil_GroupAllocateMemory(
 
653
                                             hpiHostApi->allocations, strlen(srcName) + 1 ), paInsufficientMemory );
 
654
                strcpy( deviceName, srcName );
 
655
                baseDeviceInfo->name = deviceName;
 
656
                baseDeviceInfo->hostApi = hpiHostApi->hostApiIndex;
 
657
                baseDeviceInfo->maxInputChannels = HPI_MAX_CHANNELS;
 
658
                baseDeviceInfo->maxOutputChannels = 0;
 
659
                /* Default latency values for interactive performance */
 
660
                baseDeviceInfo->defaultLowInputLatency = 0.01;
 
661
                baseDeviceInfo->defaultLowOutputLatency = -1.0;
 
662
                /* Default latency values for robust non-interactive applications (eg. playing sound files) */
 
663
                baseDeviceInfo->defaultHighInputLatency = 0.2;
 
664
                baseDeviceInfo->defaultHighOutputLatency = -1.0;
 
665
                /* HPI interface can actually handle any sampling rate to 1 Hz accuracy,
 
666
                * so this default is as good as any */
 
667
                baseDeviceInfo->defaultSampleRate = 44100;
 
668
 
 
669
                /* Store device in global PortAudio list */
 
670
                hostApi->deviceInfos[deviceIndex++] = (PaDeviceInfo *) hpiDevice;
 
671
            }
 
672
 
 
673
            /* Now add all output streams as devices (I know, the repetition is painful) */
 
674
            for( j=0; j < outStreams; ++j )
 
675
            {
 
676
                PaAsiHpiDeviceInfo *hpiDevice = &hpiDeviceList[deviceIndex];
 
677
                PaDeviceInfo *baseDeviceInfo = &hpiDevice->baseDeviceInfo;
 
678
                char srcName[72];
 
679
                char *deviceName;
 
680
 
 
681
                memset( hpiDevice, 0, sizeof(PaAsiHpiDeviceInfo) );
 
682
                /* Set implementation-specific device details */
 
683
                hpiDevice->subSys = hpiHostApi->subSys;
 
684
                hpiDevice->adapterIndex = i;
 
685
                hpiDevice->adapterType = type;
 
686
                hpiDevice->adapterVersion = version;
 
687
                hpiDevice->adapterSerialNumber = serial;
 
688
                hpiDevice->streamIndex = j;
 
689
                hpiDevice->streamIsOutput = 1;
 
690
                /* Set common PortAudio device stats */
 
691
                baseDeviceInfo->structVersion = 2;
 
692
                /* Make sure name string is owned by API info structure */
 
693
                sprintf( srcName,
 
694
                         "Adapter %d (%4X) - Output Stream %d", i+1, type, j+1 );
 
695
                PA_UNLESS_( deviceName = (char *) PaUtil_GroupAllocateMemory(
 
696
                                             hpiHostApi->allocations, strlen(srcName) + 1 ), paInsufficientMemory );
 
697
                strcpy( deviceName, srcName );
 
698
                baseDeviceInfo->name = deviceName;
 
699
                baseDeviceInfo->hostApi = hpiHostApi->hostApiIndex;
 
700
                baseDeviceInfo->maxInputChannels = 0;
 
701
                baseDeviceInfo->maxOutputChannels = HPI_MAX_CHANNELS;
 
702
                /* Default latency values for interactive performance. */
 
703
                baseDeviceInfo->defaultLowInputLatency = -1.0;
 
704
                baseDeviceInfo->defaultLowOutputLatency = 0.01;
 
705
                /* Default latency values for robust non-interactive applications (eg. playing sound files). */
 
706
                baseDeviceInfo->defaultHighInputLatency = -1.0;
 
707
                baseDeviceInfo->defaultHighOutputLatency = 0.2;
 
708
                /* HPI interface can actually handle any sampling rate to 1 Hz accuracy,
 
709
                * so this default is as good as any */
 
710
                baseDeviceInfo->defaultSampleRate = 44100;
 
711
 
 
712
                /* Store device in global PortAudio list */
 
713
                hostApi->deviceInfos[deviceIndex++] = (PaDeviceInfo *) hpiDevice;
 
714
            }
 
715
        }
 
716
    }
 
717
 
 
718
    /* Finally acknowledge checked devices */
 
719
    baseApiInfo->deviceCount = deviceIndex;
 
720
 
 
721
error:
 
722
    return result;
 
723
}
 
724
 
 
725
 
 
726
/** Initialize host API implementation.
 
727
 This is the only function exported beyond this file. It is called by PortAudio to initialize
 
728
 the host API. It stores API info, finds and registers all devices, and sets up callback and
 
729
 blocking interfaces.
 
730
 
 
731
 @param hostApi Pointer to host API struct
 
732
 
 
733
 @param hostApiIndex Index of current (HPI) host API
 
734
 
 
735
 @return PortAudio error code
 
736
 */
 
737
PaError PaAsiHpi_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex )
 
738
{
 
739
    PaError result = paNoError;
 
740
    PaAsiHpiHostApiRepresentation *hpiHostApi = NULL;
 
741
    PaHostApiInfo *baseApiInfo;
 
742
 
 
743
    /* Allocate host API structure */
 
744
    PA_UNLESS_( hpiHostApi = (PaAsiHpiHostApiRepresentation*) PaUtil_AllocateMemory(
 
745
                                 sizeof(PaAsiHpiHostApiRepresentation) ), paInsufficientMemory );
 
746
    PA_UNLESS_( hpiHostApi->allocations = PaUtil_CreateAllocationGroup(), paInsufficientMemory );
 
747
 
 
748
    hpiHostApi->hostApiIndex = hostApiIndex;
 
749
    hpiHostApi->subSys = NULL;
 
750
 
 
751
    /* Try to initialize HPI subsystem */
 
752
    if( ( hpiHostApi->subSys = HPI_SubSysCreate() ) == NULL)
 
753
    {
 
754
        /* the V19 development docs say that if an implementation
 
755
         * detects that it cannot be used, it should return a NULL
 
756
         * interface and paNoError */
 
757
        PA_DEBUG(( "Could not open HPI interface\n" ));
 
758
        result = paNoError;
 
759
        *hostApi = NULL;
 
760
        goto error;
 
761
    }
 
762
    else
 
763
    {
 
764
        HW32 hpiVersion;
 
765
        PA_ASIHPI_UNLESS_( HPI_SubSysGetVersion( hpiHostApi->subSys, &hpiVersion ), paUnanticipatedHostError );
 
766
        PA_DEBUG(( "HPI interface v%d.%02d\n",
 
767
                   hpiVersion >> 8, 10*((hpiVersion & 0xF0) >> 4) + (hpiVersion & 0x0F) ));
 
768
    }
 
769
 
 
770
    *hostApi = &hpiHostApi->baseHostApiRep;
 
771
    baseApiInfo = &((*hostApi)->info);
 
772
    /* Fill in common API details */
 
773
    baseApiInfo->structVersion = 1;
 
774
    baseApiInfo->type = paAudioScienceHPI;
 
775
    baseApiInfo->name = "AudioScience HPI";
 
776
    baseApiInfo->deviceCount = 0;
 
777
    baseApiInfo->defaultInputDevice = paNoDevice;
 
778
    baseApiInfo->defaultOutputDevice = paNoDevice;
 
779
 
 
780
    PA_ENSURE_( PaAsiHpi_BuildDeviceList( hpiHostApi ) );
 
781
 
 
782
    (*hostApi)->Terminate = Terminate;
 
783
    (*hostApi)->OpenStream = OpenStream;
 
784
    (*hostApi)->IsFormatSupported = IsFormatSupported;
 
785
 
 
786
    PaUtil_InitializeStreamInterface( &hpiHostApi->callbackStreamInterface, CloseStream, StartStream,
 
787
                                      StopStream, AbortStream, IsStreamStopped, IsStreamActive,
 
788
                                      GetStreamTime, GetStreamCpuLoad,
 
789
                                      PaUtil_DummyRead, PaUtil_DummyWrite,
 
790
                                      PaUtil_DummyGetReadAvailable, PaUtil_DummyGetWriteAvailable );
 
791
 
 
792
    PaUtil_InitializeStreamInterface( &hpiHostApi->blockingStreamInterface, CloseStream, StartStream,
 
793
                                      StopStream, AbortStream, IsStreamStopped, IsStreamActive,
 
794
                                      GetStreamTime, PaUtil_DummyGetCpuLoad,
 
795
                                      ReadStream, WriteStream, GetStreamReadAvailable, GetStreamWriteAvailable );
 
796
 
 
797
    /* Store identity of main thread */
 
798
    PA_ENSURE_( PaUnixThreading_Initialize() );
 
799
 
 
800
    return result;
 
801
error:
 
802
    /* Clean up memory */
 
803
    Terminate( (PaUtilHostApiRepresentation *)hpiHostApi );
 
804
    return result;
 
805
}
 
806
 
 
807
 
 
808
/** Terminate host API implementation.
 
809
 This closes all HPI adapters and frees the HPI subsystem. It also frees the host API struct
 
810
 memory. It should be called once for every PaAsiHpi_Initialize call.
 
811
 
 
812
 @param Pointer to host API struct
 
813
 */
 
814
static void Terminate( struct PaUtilHostApiRepresentation *hostApi )
 
815
{
 
816
    PaAsiHpiHostApiRepresentation *hpiHostApi = (PaAsiHpiHostApiRepresentation*)hostApi;
 
817
    int i;
 
818
    PaError result = paNoError;
 
819
 
 
820
    if( hpiHostApi )
 
821
    {
 
822
        /* Get rid of HPI-specific structures */
 
823
        if( hpiHostApi->subSys )
 
824
        {
 
825
            HW16 lastAdapterIndex = HPI_MAX_ADAPTERS;
 
826
            /* Iterate through device list and close adapters */
 
827
            for( i=0; i < hostApi->info.deviceCount; ++i )
 
828
            {
 
829
                PaAsiHpiDeviceInfo *hpiDevice = (PaAsiHpiDeviceInfo *) hostApi->deviceInfos[ i ];
 
830
                /* Close adapter only if it differs from previous one */
 
831
                if( hpiDevice->adapterIndex != lastAdapterIndex )
 
832
                {
 
833
                    /* Ignore errors (report only during debugging) */
 
834
                    PA_ASIHPI_UNLESS_( HPI_AdapterClose( hpiHostApi->subSys,
 
835
                                                         hpiDevice->adapterIndex ), paNoError );
 
836
                    lastAdapterIndex = hpiDevice->adapterIndex;
 
837
                }
 
838
            }
 
839
            /* Finally dismantle HPI subsystem */
 
840
            HPI_SubSysFree( hpiHostApi->subSys );
 
841
        }
 
842
 
 
843
        if( hpiHostApi->allocations )
 
844
        {
 
845
            PaUtil_FreeAllAllocations( hpiHostApi->allocations );
 
846
            PaUtil_DestroyAllocationGroup( hpiHostApi->allocations );
 
847
        }
 
848
 
 
849
        PaUtil_FreeMemory( hpiHostApi );
 
850
    }
 
851
error:
 
852
    return;
 
853
}
 
854
 
 
855
 
 
856
/** Converts PortAudio sample format to equivalent HPI format.
 
857
 
 
858
 @param paFormat PortAudio sample format
 
859
 
 
860
 @return HPI sample format
 
861
 */
 
862
static HW16 PaAsiHpi_PaToHpiFormat( PaSampleFormat paFormat )
 
863
{
 
864
    /* Ignore interleaving flag */
 
865
    switch( paFormat & ~paNonInterleaved )
 
866
    {
 
867
    case paFloat32:
 
868
        return HPI_FORMAT_PCM32_FLOAT;
 
869
 
 
870
    case paInt32:
 
871
        return HPI_FORMAT_PCM32_SIGNED;
 
872
 
 
873
    case paInt24:
 
874
        return HPI_FORMAT_PCM24_SIGNED;
 
875
 
 
876
    case paInt16:
 
877
        return HPI_FORMAT_PCM16_SIGNED;
 
878
 
 
879
    case paUInt8:
 
880
        return HPI_FORMAT_PCM8_UNSIGNED;
 
881
 
 
882
        /* Default is 16-bit signed */
 
883
    case paInt8:
 
884
    default:
 
885
        return HPI_FORMAT_PCM16_SIGNED;
 
886
    }
 
887
}
 
888
 
 
889
 
 
890
/** Converts HPI sample format to equivalent PortAudio format.
 
891
 
 
892
 @param paFormat HPI sample format
 
893
 
 
894
 @return PortAudio sample format
 
895
 */
 
896
static PaSampleFormat PaAsiHpi_HpiToPaFormat( HW16 hpiFormat )
 
897
{
 
898
    switch( hpiFormat )
 
899
    {
 
900
    case HPI_FORMAT_PCM32_FLOAT:
 
901
        return paFloat32;
 
902
 
 
903
    case HPI_FORMAT_PCM32_SIGNED:
 
904
        return paInt32;
 
905
 
 
906
    case HPI_FORMAT_PCM24_SIGNED:
 
907
        return paInt24;
 
908
 
 
909
    case HPI_FORMAT_PCM16_SIGNED:
 
910
        return paInt16;
 
911
 
 
912
    case HPI_FORMAT_PCM8_UNSIGNED:
 
913
        return paUInt8;
 
914
 
 
915
        /* Default is custom format (e.g. for HPI MP3 format) */
 
916
    default:
 
917
        return paCustomFormat;
 
918
    }
 
919
}
 
920
 
 
921
 
 
922
/** Creates HPI format struct based on PortAudio parameters.
 
923
 This also does some checks to see whether the desired format is valid, and whether
 
924
 the device allows it. This only checks the format of one half (input or output) of the
 
925
 PortAudio stream.
 
926
 
 
927
 @param hostApi Pointer to host API struct
 
928
 
 
929
 @param parameters Pointer to stream parameter struct
 
930
 
 
931
 @param sampleRate Desired sample rate
 
932
 
 
933
 @param hpiDevice Pointer to HPI device struct
 
934
 
 
935
 @param hpiFormat Resulting HPI format returned here
 
936
 
 
937
 @return PortAudio error code (typically indicating a problem with stream format)
 
938
 */
 
939
static PaError PaAsiHpi_CreateFormat( struct PaUtilHostApiRepresentation *hostApi,
 
940
                                      const PaStreamParameters *parameters, double sampleRate,
 
941
                                      PaAsiHpiDeviceInfo **hpiDevice, HPI_FORMAT *hpiFormat )
 
942
{
 
943
    int maxChannelCount = 0;
 
944
    PaSampleFormat hostSampleFormat = 0;
 
945
    HW16 hpiError = 0;
 
946
 
 
947
    /* Unless alternate device specification is supported, reject the use of
 
948
       paUseHostApiSpecificDeviceSpecification */
 
949
    if( parameters->device == paUseHostApiSpecificDeviceSpecification )
 
950
        return paInvalidDevice;
 
951
    else
 
952
    {
 
953
        assert( parameters->device < hostApi->info.deviceCount );
 
954
        *hpiDevice = (PaAsiHpiDeviceInfo*) hostApi->deviceInfos[ parameters->device ];
 
955
    }
 
956
 
 
957
    /* Validate streamInfo - this implementation doesn't use custom stream info */
 
958
    if( parameters->hostApiSpecificStreamInfo )
 
959
        return paIncompatibleHostApiSpecificStreamInfo;
 
960
 
 
961
    /* Check that device can support channel count */
 
962
    if( (*hpiDevice)->streamIsOutput )
 
963
    {
 
964
        maxChannelCount = (*hpiDevice)->baseDeviceInfo.maxOutputChannels;
 
965
    }
 
966
    else
 
967
    {
 
968
        maxChannelCount = (*hpiDevice)->baseDeviceInfo.maxInputChannels;
 
969
    }
 
970
    if( (maxChannelCount == 0) || (parameters->channelCount > maxChannelCount) )
 
971
        return paInvalidChannelCount;
 
972
 
 
973
    /* All standard sample formats are supported by the buffer adapter,
 
974
       and this implementation doesn't support any custom sample formats */
 
975
    if( parameters->sampleFormat & paCustomFormat )
 
976
        return paSampleFormatNotSupported;
 
977
 
 
978
    /* Switch to closest HPI native format */
 
979
    hostSampleFormat = PaUtil_SelectClosestAvailableFormat(PA_ASIHPI_AVAILABLE_FORMATS_,
 
980
                       parameters->sampleFormat );
 
981
    /* Setup format + info objects */
 
982
    hpiError = HPI_FormatCreate( hpiFormat, (HW16)parameters->channelCount,
 
983
                                 PaAsiHpi_PaToHpiFormat( hostSampleFormat ),
 
984
                                 (HW32)sampleRate, 0, 0 );
 
985
    if( hpiError )
 
986
    {
 
987
        PA_ASIHPI_REPORT_ERROR_( hpiError );
 
988
        switch( hpiError )
 
989
        {
 
990
        case HPI_ERROR_INVALID_FORMAT:
 
991
            return paSampleFormatNotSupported;
 
992
 
 
993
        case HPI_ERROR_INVALID_SAMPLERATE:
 
994
        case HPI_ERROR_INCOMPATIBLE_SAMPLERATE:
 
995
            return paInvalidSampleRate;
 
996
 
 
997
        case HPI_ERROR_INVALID_CHANNELS:
 
998
            return paInvalidChannelCount;
 
999
        }
 
1000
    }
 
1001
 
 
1002
    return paNoError;
 
1003
}
 
1004
 
 
1005
 
 
1006
/** Open HPI input stream with given format.
 
1007
 This attempts to open HPI input stream with desired format. If the format is not supported
 
1008
 or the device is unavailable, the stream is closed and a PortAudio error code is returned.
 
1009
 
 
1010
 @param hostApi Pointer to host API struct
 
1011
 
 
1012
 @param hpiDevice Pointer to HPI device struct
 
1013
 
 
1014
 @param hpiFormat Pointer to HPI format struct
 
1015
 
 
1016
 @return PortAudio error code (typically indicating a problem with stream format or device)
 
1017
*/
 
1018
static PaError PaAsiHpi_OpenInput( struct PaUtilHostApiRepresentation *hostApi,
 
1019
                                   const PaAsiHpiDeviceInfo *hpiDevice, const HPI_FORMAT *hpiFormat,
 
1020
                                   HPI_HISTREAM *hpiStream )
 
1021
{
 
1022
    PaAsiHpiHostApiRepresentation *hpiHostApi = (PaAsiHpiHostApiRepresentation*)hostApi;
 
1023
    PaError result = paNoError;
 
1024
    HW16 hpiError = 0;
 
1025
 
 
1026
    /* Catch misplaced output devices, as they typically have 0 input channels */
 
1027
    PA_UNLESS_( !hpiDevice->streamIsOutput, paInvalidChannelCount );
 
1028
    /* Try to open input stream */
 
1029
    PA_ASIHPI_UNLESS_( HPI_InStreamOpen( hpiHostApi->subSys, hpiDevice->adapterIndex,
 
1030
                                         hpiDevice->streamIndex, hpiStream ), paDeviceUnavailable );
 
1031
    /* Set input format (checking it in the process) */
 
1032
    /* Could also use HPI_InStreamQueryFormat, but this economizes the process */
 
1033
    hpiError = HPI_InStreamSetFormat( hpiHostApi->subSys, *hpiStream, (HPI_FORMAT*)hpiFormat );
 
1034
    if( hpiError )
 
1035
    {
 
1036
        PA_ASIHPI_REPORT_ERROR_( hpiError );
 
1037
        PA_ASIHPI_UNLESS_( HPI_InStreamClose( hpiHostApi->subSys, *hpiStream ), paNoError );
 
1038
        switch( hpiError )
 
1039
        {
 
1040
        case HPI_ERROR_INVALID_FORMAT:
 
1041
            return paSampleFormatNotSupported;
 
1042
 
 
1043
        case HPI_ERROR_INVALID_SAMPLERATE:
 
1044
        case HPI_ERROR_INCOMPATIBLE_SAMPLERATE:
 
1045
            return paInvalidSampleRate;
 
1046
 
 
1047
        case HPI_ERROR_INVALID_CHANNELS:
 
1048
            return paInvalidChannelCount;
 
1049
 
 
1050
        default:
 
1051
            /* In case anything else went wrong */
 
1052
            return paInvalidDevice;
 
1053
        }
 
1054
    }
 
1055
 
 
1056
error:
 
1057
    return result;
 
1058
}
 
1059
 
 
1060
 
 
1061
/** Open HPI output stream with given format.
 
1062
 This attempts to open HPI output stream with desired format. If the format is not supported
 
1063
 or the device is unavailable, the stream is closed and a PortAudio error code is returned.
 
1064
 
 
1065
 @param hostApi Pointer to host API struct
 
1066
 
 
1067
 @param hpiDevice Pointer to HPI device struct
 
1068
 
 
1069
 @param hpiFormat Pointer to HPI format struct
 
1070
 
 
1071
 @return PortAudio error code (typically indicating a problem with stream format or device)
 
1072
*/
 
1073
static PaError PaAsiHpi_OpenOutput( struct PaUtilHostApiRepresentation *hostApi,
 
1074
                                    const PaAsiHpiDeviceInfo *hpiDevice, const HPI_FORMAT *hpiFormat,
 
1075
                                    HPI_HOSTREAM *hpiStream )
 
1076
{
 
1077
    PaAsiHpiHostApiRepresentation *hpiHostApi = (PaAsiHpiHostApiRepresentation*)hostApi;
 
1078
    PaError result = paNoError;
 
1079
    HW16 hpiError = 0;
 
1080
 
 
1081
    /* Catch misplaced input devices, as they typically have 0 output channels */
 
1082
    PA_UNLESS_( hpiDevice->streamIsOutput, paInvalidChannelCount );
 
1083
    /* Try to open output stream */
 
1084
    PA_ASIHPI_UNLESS_( HPI_OutStreamOpen( hpiHostApi->subSys, hpiDevice->adapterIndex,
 
1085
                                          hpiDevice->streamIndex, hpiStream ), paDeviceUnavailable );
 
1086
 
 
1087
    /* Check output format (format is set on first write to output stream) */
 
1088
    hpiError = HPI_OutStreamQueryFormat( hpiHostApi->subSys, *hpiStream, (HPI_FORMAT*)hpiFormat );
 
1089
    if( hpiError )
 
1090
    {
 
1091
        PA_ASIHPI_REPORT_ERROR_( hpiError );
 
1092
        PA_ASIHPI_UNLESS_( HPI_OutStreamClose( hpiHostApi->subSys, *hpiStream ), paNoError );
 
1093
        switch( hpiError )
 
1094
        {
 
1095
        case HPI_ERROR_INVALID_FORMAT:
 
1096
            return paSampleFormatNotSupported;
 
1097
 
 
1098
        case HPI_ERROR_INVALID_SAMPLERATE:
 
1099
        case HPI_ERROR_INCOMPATIBLE_SAMPLERATE:
 
1100
            return paInvalidSampleRate;
 
1101
 
 
1102
        case HPI_ERROR_INVALID_CHANNELS:
 
1103
            return paInvalidChannelCount;
 
1104
 
 
1105
        default:
 
1106
            /* In case anything else went wrong */
 
1107
            return paInvalidDevice;
 
1108
        }
 
1109
    }
 
1110
 
 
1111
error:
 
1112
    return result;
 
1113
}
 
1114
 
 
1115
 
 
1116
/** Checks whether the desired stream formats and devices are supported
 
1117
 (for both input and output).
 
1118
 This is done by actually opening the appropriate HPI streams and closing them again.
 
1119
 
 
1120
 @param hostApi Pointer to host API struct
 
1121
 
 
1122
 @param inputParameters Pointer to stream parameter struct for input side of stream
 
1123
 
 
1124
 @param outputParameters Pointer to stream parameter struct for output side of stream
 
1125
 
 
1126
 @param sampleRate Desired sample rate
 
1127
 
 
1128
 @return PortAudio error code (paFormatIsSupported on success)
 
1129
 */
 
1130
static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
 
1131
                                  const PaStreamParameters *inputParameters,
 
1132
                                  const PaStreamParameters *outputParameters,
 
1133
                                  double sampleRate )
 
1134
{
 
1135
    PaError result = paFormatIsSupported;
 
1136
    PaAsiHpiHostApiRepresentation *hpiHostApi = (PaAsiHpiHostApiRepresentation*)hostApi;
 
1137
    PaAsiHpiDeviceInfo *hpiDevice = NULL;
 
1138
    HPI_FORMAT hpiFormat;
 
1139
 
 
1140
    /* Input stream */
 
1141
    if( inputParameters )
 
1142
    {
 
1143
        HPI_HISTREAM hpiStream;
 
1144
        PA_DEBUG(( "%s: Checking input params: dev=%d, sr=%d, chans=%d, fmt=%d\n",
 
1145
                   __FUNCTION__, inputParameters->device, (int)sampleRate,
 
1146
                   inputParameters->channelCount, inputParameters->sampleFormat ));
 
1147
        /* Create and validate format */
 
1148
        PA_ENSURE_( PaAsiHpi_CreateFormat( hostApi, inputParameters, sampleRate,
 
1149
                                           &hpiDevice, &hpiFormat ) );
 
1150
        /* Open stream to further check format */
 
1151
        PA_ENSURE_( PaAsiHpi_OpenInput( hostApi, hpiDevice, &hpiFormat, &hpiStream ) );
 
1152
        /* Close stream again */
 
1153
        PA_ASIHPI_UNLESS_( HPI_InStreamClose( hpiHostApi->subSys, hpiStream ), paNoError );
 
1154
    }
 
1155
 
 
1156
    /* Output stream */
 
1157
    if( outputParameters )
 
1158
    {
 
1159
        HPI_HOSTREAM hpiStream;
 
1160
        PA_DEBUG(( "%s: Checking output params: dev=%d, sr=%d, chans=%d, fmt=%d\n",
 
1161
                   __FUNCTION__, outputParameters->device, (int)sampleRate,
 
1162
                   outputParameters->channelCount, outputParameters->sampleFormat ));
 
1163
        /* Create and validate format */
 
1164
        PA_ENSURE_( PaAsiHpi_CreateFormat( hostApi, outputParameters, sampleRate,
 
1165
                                           &hpiDevice, &hpiFormat ) );
 
1166
        /* Open stream to further check format */
 
1167
        PA_ENSURE_( PaAsiHpi_OpenOutput( hostApi, hpiDevice, &hpiFormat, &hpiStream ) );
 
1168
        /* Close stream again */
 
1169
        PA_ASIHPI_UNLESS_( HPI_OutStreamClose( hpiHostApi->subSys, hpiStream ), paNoError );
 
1170
    }
 
1171
 
 
1172
error:
 
1173
    return result;
 
1174
}
 
1175
 
 
1176
/* ---------------------------- Stream Interface ---------------------------- */
 
1177
 
 
1178
/** Obtain HPI stream information.
 
1179
 This obtains info such as stream state and available data/space in buffers. It also
 
1180
 estimates whether an underflow or overflow occurred.
 
1181
 
 
1182
 @param streamComp Pointer to stream component (input or output) to query
 
1183
 
 
1184
 @param info Pointer to stream info struct that will contain result
 
1185
 
 
1186
 @return PortAudio error code (either paNoError, paDeviceUnavailable or paUnanticipatedHostError)
 
1187
 */
 
1188
static PaError PaAsiHpi_GetStreamInfo( PaAsiHpiStreamComponent *streamComp, PaAsiHpiStreamInfo *info )
 
1189
{
 
1190
    PaError result = paDeviceUnavailable;
 
1191
    HW16 state;
 
1192
    HW32 bufferSize, dataSize, frameCounter, auxDataSize, threshold;
 
1193
    HW32 hwBufferSize, hwDataSize;
 
1194
 
 
1195
    assert( streamComp );
 
1196
    assert( info );
 
1197
 
 
1198
    /* First blank the stream info struct, in case something goes wrong below.
 
1199
       This saves the caller from initializing the struct. */
 
1200
    info->state = 0;
 
1201
    info->bufferSize = 0;
 
1202
    info->dataSize = 0;
 
1203
    info->frameCounter = 0;
 
1204
    info->auxDataSize = 0;
 
1205
    info->totalBufferedData = 0;
 
1206
    info->availableFrames = 0;
 
1207
    info->underflow = 0;
 
1208
    info->overflow = 0;
 
1209
 
 
1210
    if( streamComp->hpiDevice && streamComp->hpiStream )
 
1211
    {
 
1212
        /* Obtain detailed stream info (either input or output) */
 
1213
        if( streamComp->hpiDevice->streamIsOutput )
 
1214
        {
 
1215
            PA_ASIHPI_UNLESS_( HPI_OutStreamGetInfoEx( streamComp->hpiDevice->subSys,
 
1216
                               streamComp->hpiStream,
 
1217
                               &state, &bufferSize, &dataSize, &frameCounter,
 
1218
                               &auxDataSize ), paUnanticipatedHostError );
 
1219
        }
 
1220
        else
 
1221
        {
 
1222
            PA_ASIHPI_UNLESS_( HPI_InStreamGetInfoEx( streamComp->hpiDevice->subSys,
 
1223
                               streamComp->hpiStream,
 
1224
                               &state, &bufferSize, &dataSize, &frameCounter,
 
1225
                               &auxDataSize ), paUnanticipatedHostError );
 
1226
        }
 
1227
        /* Load stream info */
 
1228
        info->state = state;
 
1229
        info->bufferSize = bufferSize;
 
1230
        info->dataSize = dataSize;
 
1231
        info->frameCounter = frameCounter;
 
1232
        info->auxDataSize = auxDataSize;
 
1233
        /* Determine total buffered data */
 
1234
        info->totalBufferedData = dataSize;
 
1235
        if( streamComp->hostBufferSize > 0 )
 
1236
            info->totalBufferedData += auxDataSize;
 
1237
        info->totalBufferedData /= streamComp->bytesPerFrame;
 
1238
        /* Determine immediately available frames */
 
1239
        info->availableFrames = streamComp->hpiDevice->streamIsOutput ?
 
1240
                                bufferSize - dataSize : dataSize;
 
1241
        info->availableFrames /= streamComp->bytesPerFrame;
 
1242
        /* Minimum space/data required in buffers */
 
1243
        threshold = PA_MIN( streamComp->tempBufferSize,
 
1244
                            streamComp->bytesPerFrame * PA_ASIHPI_MIN_FRAMES_ );
 
1245
        /* Obtain hardware buffer stats first, to simplify things */
 
1246
        hwBufferSize = streamComp->hardwareBufferSize;
 
1247
        hwDataSize = streamComp->hostBufferSize > 0 ? auxDataSize : dataSize;
 
1248
        /* Underflow is a bit tricky */
 
1249
        info->underflow = streamComp->hpiDevice->streamIsOutput ?
 
1250
                          /* Stream seems to start in drained state sometimes, so ignore initial underflow */
 
1251
                          (frameCounter > 0) && ( (state == HPI_STATE_DRAINED) || (hwDataSize == 0) ) :
 
1252
                          /* Input streams check the first-level (host) buffer for underflow */
 
1253
                          (state != HPI_STATE_STOPPED) && (dataSize < threshold);
 
1254
        /* Check for overflow in second-level (hardware) buffer for both input and output */
 
1255
        info->overflow = (state != HPI_STATE_STOPPED) && (hwBufferSize - hwDataSize < threshold);
 
1256
 
 
1257
        return paNoError;
 
1258
    }
 
1259
 
 
1260
error:
 
1261
    return result;
 
1262
}
 
1263
 
 
1264
 
 
1265
/** Display stream component information for debugging purposes.
 
1266
 
 
1267
 @param streamComp Pointer to stream component (input or output) to query
 
1268
 
 
1269
 @param stream Pointer to stream struct which contains the component above
 
1270
 */
 
1271
static void PaAsiHpi_StreamComponentDump( PaAsiHpiStreamComponent *streamComp,
 
1272
        PaAsiHpiStream *stream )
 
1273
{
 
1274
    PaAsiHpiStreamInfo streamInfo;
 
1275
 
 
1276
    assert( streamComp );
 
1277
    assert( stream );
 
1278
 
 
1279
    /* Name of soundcard/device used by component */
 
1280
    PA_DEBUG(( "device: %s\n", streamComp->hpiDevice->baseDeviceInfo.name ));
 
1281
    /* Unfortunately some overlap between input and output here */
 
1282
    if( streamComp->hpiDevice->streamIsOutput )
 
1283
    {
 
1284
        /* Settings on the user side (as experienced by user callback) */
 
1285
        PA_DEBUG(( "user: %d-bit, %d ",
 
1286
                   8*stream->bufferProcessor.bytesPerUserOutputSample,
 
1287
                   stream->bufferProcessor.outputChannelCount));
 
1288
        if( stream->bufferProcessor.userOutputIsInterleaved )
 
1289
        {
 
1290
            PA_DEBUG(( "interleaved channels, " ));
 
1291
        }
 
1292
        else
 
1293
        {
 
1294
            PA_DEBUG(( "non-interleaved channels, " ));
 
1295
        }
 
1296
        PA_DEBUG(( "%d frames/buffer, latency = %5.1f ms\n",
 
1297
                   stream->bufferProcessor.framesPerUserBuffer,
 
1298
                   1000*stream->baseStreamRep.streamInfo.outputLatency ));
 
1299
        /* Settings on the host side (internal to PortAudio host API) */
 
1300
        PA_DEBUG(( "host: %d-bit, %d interleaved channels, %d frames/buffer ",
 
1301
                   8*stream->bufferProcessor.bytesPerHostOutputSample,
 
1302
                   stream->bufferProcessor.outputChannelCount,
 
1303
                   stream->bufferProcessor.framesPerHostBuffer ));
 
1304
    }
 
1305
    else
 
1306
    {
 
1307
        /* Settings on the user side (as experienced by user callback) */
 
1308
        PA_DEBUG(( "user: %d-bit, %d ",
 
1309
                   8*stream->bufferProcessor.bytesPerUserInputSample,
 
1310
                   stream->bufferProcessor.inputChannelCount));
 
1311
        if( stream->bufferProcessor.userInputIsInterleaved )
 
1312
        {
 
1313
            PA_DEBUG(( "interleaved channels, " ));
 
1314
        }
 
1315
        else
 
1316
        {
 
1317
            PA_DEBUG(( "non-interleaved channels, " ));
 
1318
        }
 
1319
        PA_DEBUG(( "%d frames/buffer, latency = %5.1f ms\n",
 
1320
                   stream->bufferProcessor.framesPerUserBuffer,
 
1321
                   1000*stream->baseStreamRep.streamInfo.inputLatency ));
 
1322
        /* Settings on the host side (internal to PortAudio host API) */
 
1323
        PA_DEBUG(( "host: %d-bit, %d interleaved channels, %d frames/buffer ",
 
1324
                   8*stream->bufferProcessor.bytesPerHostInputSample,
 
1325
                   stream->bufferProcessor.inputChannelCount,
 
1326
                   stream->bufferProcessor.framesPerHostBuffer ));
 
1327
    }
 
1328
    switch( stream->bufferProcessor.hostBufferSizeMode )
 
1329
    {
 
1330
    case paUtilFixedHostBufferSize:
 
1331
        PA_DEBUG(( "[fixed] " ));
 
1332
        break;
 
1333
    case paUtilBoundedHostBufferSize:
 
1334
        PA_DEBUG(( "[bounded] " ));
 
1335
        break;
 
1336
    case paUtilUnknownHostBufferSize:
 
1337
        PA_DEBUG(( "[unknown] " ));
 
1338
        break;
 
1339
    case paUtilVariableHostBufferSizePartialUsageAllowed:
 
1340
        PA_DEBUG(( "[variable] " ));
 
1341
        break;
 
1342
    }
 
1343
    PA_DEBUG(( "(%d max)\n", streamComp->tempBufferSize / streamComp->bytesPerFrame ));
 
1344
    /* HPI hardware settings */
 
1345
    PA_DEBUG(( "HPI: adapter %d stream %d, %d-bit, %d-channel, %d Hz\n",
 
1346
               streamComp->hpiDevice->adapterIndex, streamComp->hpiDevice->streamIndex,
 
1347
               8 * streamComp->bytesPerFrame / streamComp->hpiFormat.wChannels,
 
1348
               streamComp->hpiFormat.wChannels,
 
1349
               streamComp->hpiFormat.dwSampleRate ));
 
1350
    /* Stream state and buffer levels */
 
1351
    PA_DEBUG(( "HPI: " ));
 
1352
    PaAsiHpi_GetStreamInfo( streamComp, &streamInfo );
 
1353
    switch( streamInfo.state )
 
1354
    {
 
1355
    case HPI_STATE_STOPPED:
 
1356
        PA_DEBUG(( "[STOPPED] " ));
 
1357
        break;
 
1358
    case HPI_STATE_PLAYING:
 
1359
        PA_DEBUG(( "[PLAYING] " ));
 
1360
        break;
 
1361
    case HPI_STATE_RECORDING:
 
1362
        PA_DEBUG(( "[RECORDING] " ));
 
1363
        break;
 
1364
    case HPI_STATE_DRAINED:
 
1365
        PA_DEBUG(( "[DRAINED] " ));
 
1366
        break;
 
1367
    default:
 
1368
        PA_DEBUG(( "[unknown state] " ));
 
1369
        break;
 
1370
    }
 
1371
    if( streamComp->hostBufferSize )
 
1372
    {
 
1373
        PA_DEBUG(( "host = %d/%d B, ", streamInfo.dataSize, streamComp->hostBufferSize ));
 
1374
        PA_DEBUG(( "hw = %d/%d (%d) B, ", streamInfo.auxDataSize,
 
1375
                   streamComp->hardwareBufferSize, streamComp->outputBufferCap ));
 
1376
    }
 
1377
    else
 
1378
    {
 
1379
        PA_DEBUG(( "hw = %d/%d B, ", streamInfo.dataSize, streamComp->hardwareBufferSize ));
 
1380
    }
 
1381
    PA_DEBUG(( "count = %d", streamInfo.frameCounter ));
 
1382
    if( streamInfo.overflow )
 
1383
    {
 
1384
        PA_DEBUG(( " [overflow]" ));
 
1385
    }
 
1386
    else if( streamInfo.underflow )
 
1387
    {
 
1388
        PA_DEBUG(( " [underflow]" ));
 
1389
    }
 
1390
    PA_DEBUG(( "\n" ));
 
1391
}
 
1392
 
 
1393
 
 
1394
/** Display stream information for debugging purposes.
 
1395
 
 
1396
 @param stream Pointer to stream to query
 
1397
 */
 
1398
static void PaAsiHpi_StreamDump( PaAsiHpiStream *stream )
 
1399
{
 
1400
    assert( stream );
 
1401
 
 
1402
    PA_DEBUG(( "\n------------------------- STREAM INFO FOR %p ---------------------------\n", stream ));
 
1403
    /* General stream info (input+output) */
 
1404
    if( stream->baseStreamRep.streamCallback )
 
1405
    {
 
1406
        PA_DEBUG(( "[callback] " ));
 
1407
    }
 
1408
    else
 
1409
    {
 
1410
        PA_DEBUG(( "[blocking] " ));
 
1411
    }
 
1412
    PA_DEBUG(( "sr=%d Hz, poll=%d ms, max %d frames/buf ",
 
1413
               (int)stream->baseStreamRep.streamInfo.sampleRate,
 
1414
               stream->pollingInterval, stream->maxFramesPerHostBuffer ));
 
1415
    switch( stream->state )
 
1416
    {
 
1417
    case paAsiHpiStoppedState:
 
1418
        PA_DEBUG(( "[stopped]\n" ));
 
1419
        break;
 
1420
    case paAsiHpiActiveState:
 
1421
        PA_DEBUG(( "[active]\n" ));
 
1422
        break;
 
1423
    case paAsiHpiCallbackFinishedState:
 
1424
        PA_DEBUG(( "[cb fin]\n" ));
 
1425
        break;
 
1426
    default:
 
1427
        PA_DEBUG(( "[unknown state]\n" ));
 
1428
        break;
 
1429
    }
 
1430
    if( stream->callbackMode )
 
1431
    {
 
1432
        PA_DEBUG(( "cb info: thread=%p, cbAbort=%d, cbFinished=%d\n",
 
1433
                   stream->thread.thread, stream->callbackAbort, stream->callbackFinished ));
 
1434
    }
 
1435
 
 
1436
    PA_DEBUG(( "----------------------------------- Input  ------------------------------------\n" ));
 
1437
    if( stream->input )
 
1438
    {
 
1439
        PaAsiHpi_StreamComponentDump( stream->input, stream );
 
1440
    }
 
1441
    else
 
1442
    {
 
1443
        PA_DEBUG(( "*none*\n" ));
 
1444
    }
 
1445
 
 
1446
    PA_DEBUG(( "----------------------------------- Output ------------------------------------\n" ));
 
1447
    if( stream->output )
 
1448
    {
 
1449
        PaAsiHpi_StreamComponentDump( stream->output, stream );
 
1450
    }
 
1451
    else
 
1452
    {
 
1453
        PA_DEBUG(( "*none*\n" ));
 
1454
    }
 
1455
    PA_DEBUG(( "-------------------------------------------------------------------------------\n\n" ));
 
1456
 
 
1457
}
 
1458
 
 
1459
 
 
1460
/** Determine buffer sizes and allocate appropriate stream buffers.
 
1461
 This attempts to allocate a BBM (host) buffer for the HPI stream component (either input
 
1462
 or output, as both have similar buffer needs). Not all AudioScience adapters support BBM,
 
1463
 in which case the hardware buffer has to suffice. The size of the HPI host buffer is chosen
 
1464
 as a multiple of framesPerPaHostBuffer, and also influenced by the suggested latency and the
 
1465
 estimated minimum polling interval. The HPI host and hardware buffer sizes are stored, and an
 
1466
 appropriate cap for the hardware buffer is also calculated. Finally, the temporary stream
 
1467
 buffer which serves as the PortAudio host buffer for this implementation is allocated.
 
1468
 This buffer contains an integer number of user buffers, to simplify buffer adaption in the
 
1469
 buffer processor. The function returns paBufferTooBig if the HPI interface cannot allocate
 
1470
 an HPI host buffer of the desired size.
 
1471
 
 
1472
 @param streamComp Pointer to stream component struct
 
1473
 
 
1474
 @param pollingInterval Polling interval for stream, in milliseconds
 
1475
 
 
1476
 @param framesPerPaHostBuffer Size of PortAudio host buffer, in frames
 
1477
 
 
1478
 @param suggestedLatency Suggested latency for stream component, in seconds
 
1479
 
 
1480
 @return PortAudio error code (possibly paBufferTooBig or paInsufficientMemory)
 
1481
 */
 
1482
static PaError PaAsiHpi_SetupBuffers( PaAsiHpiStreamComponent *streamComp, HW32 pollingInterval,
 
1483
                                      unsigned long framesPerPaHostBuffer, PaTime suggestedLatency )
 
1484
{
 
1485
    PaError result = paNoError;
 
1486
    PaAsiHpiStreamInfo streamInfo;
 
1487
    unsigned long hpiBufferSize = 0, paHostBufferSize = 0;
 
1488
 
 
1489
    assert( streamComp );
 
1490
    assert( streamComp->hpiDevice );
 
1491
 
 
1492
    /* Obtain size of hardware buffer of HPI stream, since we will be activating BBM shortly
 
1493
       and afterwards the buffer size will refer to the BBM (host-side) buffer.
 
1494
       This is necessary to enable reliable detection of xruns. */
 
1495
    PA_ENSURE_( PaAsiHpi_GetStreamInfo( streamComp, &streamInfo ) );
 
1496
    streamComp->hardwareBufferSize = streamInfo.bufferSize;
 
1497
    hpiBufferSize = streamInfo.bufferSize;
 
1498
 
 
1499
    /* Check if BBM (background bus mastering) is to be enabled */
 
1500
    if( PA_ASIHPI_USE_BBM_ )
 
1501
    {
 
1502
        HW32 bbmBufferSize = 0, preLatencyBufferSize = 0;
 
1503
        HW16 hpiError = 0;
 
1504
        PaTime pollingOverhead;
 
1505
 
 
1506
        /* Check overhead of Pa_Sleep() call (minimum sleep duration in ms -> OS dependent) */
 
1507
        pollingOverhead = PaUtil_GetTime();
 
1508
        Pa_Sleep( 0 );
 
1509
        pollingOverhead = 1000*(PaUtil_GetTime() - pollingOverhead);
 
1510
        PA_DEBUG(( "polling overhead = %f ms (length of 0-second sleep)\n", pollingOverhead ));
 
1511
        /* Obtain minimum recommended size for host buffer (in bytes) */
 
1512
        PA_ASIHPI_UNLESS_( HPI_StreamEstimateBufferSize( &streamComp->hpiFormat,
 
1513
                           pollingInterval + (HW32)ceil( pollingOverhead ),
 
1514
                           &bbmBufferSize ), paUnanticipatedHostError );
 
1515
        /* BBM places more stringent requirements on buffer size (see description */
 
1516
        /* of HPI_StreamEstimateBufferSize in HPI API document) */
 
1517
        bbmBufferSize *= 3;
 
1518
        /* Make sure the BBM buffer contains multiple PA host buffers */
 
1519
        if( bbmBufferSize < 3 * streamComp->bytesPerFrame * framesPerPaHostBuffer )
 
1520
            bbmBufferSize = 3 * streamComp->bytesPerFrame * framesPerPaHostBuffer;
 
1521
        /* Try to honor latency suggested by user by growing buffer (no decrease possible) */
 
1522
        if( suggestedLatency > 0.0 )
 
1523
        {
 
1524
            PaTime bufferDuration = ((PaTime)bbmBufferSize) / streamComp->bytesPerFrame
 
1525
                                    / streamComp->hpiFormat.dwSampleRate;
 
1526
            /* Don't decrease buffer */
 
1527
            if( bufferDuration < suggestedLatency )
 
1528
            {
 
1529
                /* Save old buffer size, to be retried if new size proves too big */
 
1530
                preLatencyBufferSize = bbmBufferSize;
 
1531
                bbmBufferSize = (HW32)ceil( suggestedLatency * streamComp->bytesPerFrame
 
1532
                                            * streamComp->hpiFormat.dwSampleRate );
 
1533
            }
 
1534
        }
 
1535
        /* Choose closest memory block boundary (HPI API document states that
 
1536
        "a buffer size of Nx4096 - 20 makes the best use of memory"
 
1537
        (under the entry for HPI_StreamEstimateBufferSize)) */
 
1538
        bbmBufferSize = ((HW32)ceil((bbmBufferSize + 20)/4096.0))*4096 - 20;
 
1539
        streamComp->hostBufferSize = bbmBufferSize;
 
1540
        /* Allocate BBM host buffer (this enables bus mastering transfers in background) */
 
1541
        if( streamComp->hpiDevice->streamIsOutput )
 
1542
            hpiError = HPI_OutStreamHostBufferAllocate( streamComp->hpiDevice->subSys,
 
1543
                       streamComp->hpiStream,
 
1544
                       bbmBufferSize );
 
1545
        else
 
1546
            hpiError = HPI_InStreamHostBufferAllocate( streamComp->hpiDevice->subSys,
 
1547
                       streamComp->hpiStream,
 
1548
                       bbmBufferSize );
 
1549
        if( hpiError )
 
1550
        {
 
1551
            PA_ASIHPI_REPORT_ERROR_( hpiError );
 
1552
            /* Indicate that BBM is disabled */
 
1553
            streamComp->hostBufferSize = 0;
 
1554
            /* Retry with smaller buffer size (transfers will still work, but not via BBM) */
 
1555
            if( hpiError == HPI_ERROR_INVALID_DATASIZE )
 
1556
            {
 
1557
                /* Retry BBM allocation with smaller size if requested latency proved too big */
 
1558
                if( preLatencyBufferSize > 0 )
 
1559
                {
 
1560
                    PA_DEBUG(( "Retrying BBM allocation with smaller size (%d vs. %d bytes)\n",
 
1561
                               preLatencyBufferSize, bbmBufferSize ));
 
1562
                    bbmBufferSize = preLatencyBufferSize;
 
1563
                    if( streamComp->hpiDevice->streamIsOutput )
 
1564
                        hpiError = HPI_OutStreamHostBufferAllocate( streamComp->hpiDevice->subSys,
 
1565
                                   streamComp->hpiStream,
 
1566
                                   bbmBufferSize );
 
1567
                    else
 
1568
                        hpiError = HPI_InStreamHostBufferAllocate( streamComp->hpiDevice->subSys,
 
1569
                                   streamComp->hpiStream,
 
1570
                                   bbmBufferSize );
 
1571
                    /* Another round of error checking */
 
1572
                    if( hpiError )
 
1573
                    {
 
1574
                        PA_ASIHPI_REPORT_ERROR_( hpiError );
 
1575
                        /* No escapes this time */
 
1576
                        if( hpiError == HPI_ERROR_INVALID_DATASIZE )
 
1577
                        {
 
1578
                            result = paBufferTooBig;
 
1579
                            goto error;
 
1580
                        }
 
1581
                        else if( hpiError != HPI_ERROR_INVALID_OPERATION )
 
1582
                        {
 
1583
                            result = paUnanticipatedHostError;
 
1584
                            goto error;
 
1585
                        }
 
1586
                    }
 
1587
                    else
 
1588
                    {
 
1589
                        streamComp->hostBufferSize = bbmBufferSize;
 
1590
                        hpiBufferSize = bbmBufferSize;
 
1591
                    }
 
1592
                }
 
1593
                else
 
1594
                {
 
1595
                    result = paBufferTooBig;
 
1596
                    goto error;
 
1597
                }
 
1598
            }
 
1599
            /* If BBM not supported, foreground transfers will be used, but not a show-stopper */
 
1600
            /* Anything else is an error */
 
1601
            else if( hpiError != HPI_ERROR_INVALID_OPERATION )
 
1602
            {
 
1603
                result = paUnanticipatedHostError;
 
1604
                goto error;
 
1605
            }
 
1606
        }
 
1607
        else
 
1608
        {
 
1609
            hpiBufferSize = bbmBufferSize;
 
1610
        }
 
1611
    }
 
1612
 
 
1613
    /* Final check of buffer size */
 
1614
    paHostBufferSize = streamComp->bytesPerFrame * framesPerPaHostBuffer;
 
1615
    if( hpiBufferSize < 3*paHostBufferSize )
 
1616
    {
 
1617
        result = paBufferTooBig;
 
1618
        goto error;
 
1619
    }
 
1620
    /* Set cap on output buffer size, based on latency suggestions */
 
1621
    if( streamComp->hpiDevice->streamIsOutput )
 
1622
    {
 
1623
        PaTime latency = suggestedLatency > 0.0 ? suggestedLatency :
 
1624
                         streamComp->hpiDevice->baseDeviceInfo.defaultHighOutputLatency;
 
1625
        streamComp->outputBufferCap =
 
1626
            (HW32)ceil( latency * streamComp->bytesPerFrame * streamComp->hpiFormat.dwSampleRate );
 
1627
        /* The cap should not be too small, to prevent underflow */
 
1628
        if( streamComp->outputBufferCap < 4*paHostBufferSize )
 
1629
            streamComp->outputBufferCap = 4*paHostBufferSize;
 
1630
    }
 
1631
    else
 
1632
    {
 
1633
        streamComp->outputBufferCap = 0;
 
1634
    }
 
1635
    /* Temp buffer size should be multiple of PA host buffer size (or 1x, if using fixed blocks) */
 
1636
    streamComp->tempBufferSize = paHostBufferSize;
 
1637
    /* Allocate temp buffer */
 
1638
    PA_UNLESS_( streamComp->tempBuffer = (HW8 *)PaUtil_AllocateMemory( streamComp->tempBufferSize ),
 
1639
                paInsufficientMemory );
 
1640
error:
 
1641
    return result;
 
1642
}
 
1643
 
 
1644
 
 
1645
/** Opens PortAudio stream.
 
1646
 This determines a suitable value for framesPerBuffer, if the user didn't specify it,
 
1647
 based on the suggested latency. It then opens each requested stream direction with the
 
1648
 appropriate stream format, and allocates the required stream buffers. It sets up the
 
1649
 various PortAudio structures dealing with streams, and estimates the stream latency.
 
1650
 
 
1651
 See pa_hostapi.h for a list of validity guarantees made about OpenStream parameters.
 
1652
 
 
1653
 @param hostApi Pointer to host API struct
 
1654
 
 
1655
 @param s List of open streams, where successfully opened stream will go
 
1656
 
 
1657
 @param inputParameters Pointer to stream parameter struct for input side of stream
 
1658
 
 
1659
 @param outputParameters Pointer to stream parameter struct for output side of stream
 
1660
 
 
1661
 @param sampleRate Desired sample rate
 
1662
 
 
1663
 @param framesPerBuffer Desired number of frames per buffer passed to user callback
 
1664
                        (or chunk size for blocking stream)
 
1665
 
 
1666
 @param streamFlags Stream flags
 
1667
 
 
1668
 @param streamCallback Pointer to user callback function (zero for blocking interface)
 
1669
 
 
1670
 @param userData Pointer to user data that will be passed to callback function along with data
 
1671
 
 
1672
 @return PortAudio error code
 
1673
*/
 
1674
static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
 
1675
                           PaStream **s,
 
1676
                           const PaStreamParameters *inputParameters,
 
1677
                           const PaStreamParameters *outputParameters,
 
1678
                           double sampleRate,
 
1679
                           unsigned long framesPerBuffer,
 
1680
                           PaStreamFlags streamFlags,
 
1681
                           PaStreamCallback *streamCallback,
 
1682
                           void *userData )
 
1683
{
 
1684
    PaError result = paNoError;
 
1685
    PaAsiHpiHostApiRepresentation *hpiHostApi = (PaAsiHpiHostApiRepresentation*)hostApi;
 
1686
    PaAsiHpiStream *stream = NULL;
 
1687
    unsigned long framesPerHostBuffer = framesPerBuffer;
 
1688
    int inputChannelCount = 0, outputChannelCount = 0;
 
1689
    PaSampleFormat inputSampleFormat = 0, outputSampleFormat = 0;
 
1690
    PaSampleFormat hostInputSampleFormat = 0, hostOutputSampleFormat = 0;
 
1691
    PaTime maxSuggestedLatency = 0.0;
 
1692
 
 
1693
    /* Validate platform-specific flags -> none expected for HPI */
 
1694
    if( (streamFlags & paPlatformSpecificFlags) != 0 )
 
1695
        return paInvalidFlag; /* unexpected platform-specific flag */
 
1696
 
 
1697
    /* Create blank stream structure */
 
1698
    PA_UNLESS_( stream = (PaAsiHpiStream *)PaUtil_AllocateMemory( sizeof(PaAsiHpiStream) ),
 
1699
                paInsufficientMemory );
 
1700
    memset( stream, 0, sizeof(PaAsiHpiStream) );
 
1701
 
 
1702
    /* If the number of frames per buffer is unspecified, we have to come up with one. */
 
1703
    if( framesPerHostBuffer == paFramesPerBufferUnspecified )
 
1704
    {
 
1705
        if( inputParameters )
 
1706
            maxSuggestedLatency = inputParameters->suggestedLatency;
 
1707
        if( outputParameters && (outputParameters->suggestedLatency > maxSuggestedLatency) )
 
1708
            maxSuggestedLatency = outputParameters->suggestedLatency;
 
1709
        /* Use suggested latency if available */
 
1710
        if( maxSuggestedLatency > 0.0 )
 
1711
            framesPerHostBuffer = (unsigned long)ceil( maxSuggestedLatency * sampleRate );
 
1712
        else
 
1713
            /* AudioScience cards like BIG buffers by default */
 
1714
            framesPerHostBuffer = 4096;
 
1715
    }
 
1716
    /* Lower bounds on host buffer size, due to polling and HPI constraints */
 
1717
    if( 1000.0*framesPerHostBuffer/sampleRate < PA_ASIHPI_MIN_POLLING_INTERVAL_ )
 
1718
        framesPerHostBuffer = (unsigned long)ceil( sampleRate * PA_ASIHPI_MIN_POLLING_INTERVAL_ / 1000.0 );
 
1719
    /*    if( framesPerHostBuffer < PA_ASIHPI_MIN_FRAMES_ )
 
1720
            framesPerHostBuffer = PA_ASIHPI_MIN_FRAMES_; */
 
1721
    /* Efficient if host buffer size is integer multiple of user buffer size */
 
1722
    if( framesPerBuffer > 0 )
 
1723
        framesPerHostBuffer = (unsigned long)ceil( (double)framesPerHostBuffer / framesPerBuffer ) * framesPerBuffer;
 
1724
    /* Buffer should always be a multiple of 4 bytes to facilitate 32-bit PCI transfers.
 
1725
     By keeping the frames a multiple of 4, this is ensured even for 8-bit mono sound. */
 
1726
    framesPerHostBuffer = (framesPerHostBuffer / 4) * 4;
 
1727
    /* Polling is based on time length (in milliseconds) of user-requested block size */
 
1728
    stream->pollingInterval = (HW32)ceil( 1000.0*framesPerHostBuffer/sampleRate );
 
1729
    assert( framesPerHostBuffer > 0 );
 
1730
 
 
1731
    /* Open underlying streams, check formats and allocate buffers */
 
1732
    if( inputParameters )
 
1733
    {
 
1734
        /* Create blank stream component structure */
 
1735
        PA_UNLESS_( stream->input = (PaAsiHpiStreamComponent *)PaUtil_AllocateMemory( sizeof(PaAsiHpiStreamComponent) ),
 
1736
                    paInsufficientMemory );
 
1737
        memset( stream->input, 0, sizeof(PaAsiHpiStreamComponent) );
 
1738
        /* Create/validate format */
 
1739
        PA_ENSURE_( PaAsiHpi_CreateFormat( hostApi, inputParameters, sampleRate,
 
1740
                                           &stream->input->hpiDevice, &stream->input->hpiFormat ) );
 
1741
        /* Open stream and set format */
 
1742
        PA_ENSURE_( PaAsiHpi_OpenInput( hostApi, stream->input->hpiDevice, &stream->input->hpiFormat,
 
1743
                                        &stream->input->hpiStream ) );
 
1744
        inputChannelCount = inputParameters->channelCount;
 
1745
        inputSampleFormat = inputParameters->sampleFormat;
 
1746
        hostInputSampleFormat = PaAsiHpi_HpiToPaFormat( stream->input->hpiFormat.wFormat );
 
1747
        stream->input->bytesPerFrame = inputChannelCount * Pa_GetSampleSize( hostInputSampleFormat );
 
1748
        assert( stream->input->bytesPerFrame > 0 );
 
1749
        /* Allocate host and temp buffers of appropriate size */
 
1750
        PA_ENSURE_( PaAsiHpi_SetupBuffers( stream->input, stream->pollingInterval,
 
1751
                                           framesPerHostBuffer, inputParameters->suggestedLatency ) );
 
1752
    }
 
1753
    if( outputParameters )
 
1754
    {
 
1755
        /* Create blank stream component structure */
 
1756
        PA_UNLESS_( stream->output = (PaAsiHpiStreamComponent *)PaUtil_AllocateMemory( sizeof(PaAsiHpiStreamComponent) ),
 
1757
                    paInsufficientMemory );
 
1758
        memset( stream->output, 0, sizeof(PaAsiHpiStreamComponent) );
 
1759
        /* Create/validate format */
 
1760
        PA_ENSURE_( PaAsiHpi_CreateFormat( hostApi, outputParameters, sampleRate,
 
1761
                                           &stream->output->hpiDevice, &stream->output->hpiFormat ) );
 
1762
        /* Open stream and check format */
 
1763
        PA_ENSURE_( PaAsiHpi_OpenOutput( hostApi, stream->output->hpiDevice,
 
1764
                                         &stream->output->hpiFormat,
 
1765
                                         &stream->output->hpiStream ) );
 
1766
        outputChannelCount = outputParameters->channelCount;
 
1767
        outputSampleFormat = outputParameters->sampleFormat;
 
1768
        hostOutputSampleFormat = PaAsiHpi_HpiToPaFormat( stream->output->hpiFormat.wFormat );
 
1769
        stream->output->bytesPerFrame = outputChannelCount * Pa_GetSampleSize( hostOutputSampleFormat );
 
1770
        /* Allocate host and temp buffers of appropriate size */
 
1771
        PA_ENSURE_( PaAsiHpi_SetupBuffers( stream->output, stream->pollingInterval,
 
1772
                                           framesPerHostBuffer, outputParameters->suggestedLatency ) );
 
1773
    }
 
1774
 
 
1775
    /* Determine maximum frames per host buffer (least common denominator of input/output) */
 
1776
    if( inputParameters && outputParameters )
 
1777
    {
 
1778
        stream->maxFramesPerHostBuffer = PA_MIN( stream->input->tempBufferSize / stream->input->bytesPerFrame,
 
1779
                                         stream->output->tempBufferSize / stream->output->bytesPerFrame );
 
1780
    }
 
1781
    else
 
1782
    {
 
1783
        stream->maxFramesPerHostBuffer = inputParameters ? stream->input->tempBufferSize / stream->input->bytesPerFrame
 
1784
                                         : stream->output->tempBufferSize / stream->output->bytesPerFrame;
 
1785
    }
 
1786
    assert( stream->maxFramesPerHostBuffer > 0 );
 
1787
    /* Initialize various other stream parameters */
 
1788
    stream->neverDropInput = streamFlags & paNeverDropInput;
 
1789
    stream->state = paAsiHpiStoppedState;
 
1790
 
 
1791
    /* Initialize either callback or blocking interface */
 
1792
    if( streamCallback )
 
1793
    {
 
1794
        PaUtil_InitializeStreamRepresentation( &stream->baseStreamRep,
 
1795
                                               &hpiHostApi->callbackStreamInterface,
 
1796
                                               streamCallback, userData );
 
1797
        stream->callbackMode = 1;
 
1798
    }
 
1799
    else
 
1800
    {
 
1801
        PaUtil_InitializeStreamRepresentation( &stream->baseStreamRep,
 
1802
                                               &hpiHostApi->blockingStreamInterface,
 
1803
                                               streamCallback, userData );
 
1804
        /* Pre-allocate non-interleaved user buffer pointers for blocking interface */
 
1805
        PA_UNLESS_( stream->blockingUserBufferCopy =
 
1806
                        PaUtil_AllocateMemory( sizeof(void *) * PA_MAX( inputChannelCount, outputChannelCount ) ),
 
1807
                    paInsufficientMemory );
 
1808
        stream->callbackMode = 0;
 
1809
    }
 
1810
    PaUtil_InitializeCpuLoadMeasurer( &stream->cpuLoadMeasurer, sampleRate );
 
1811
 
 
1812
    /* Following pa_linux_alsa's lead, we operate with fixed host buffer size by default, */
 
1813
    /* since other modes will invariably lead to block adaption (maybe Bounded better?) */
 
1814
    PA_ENSURE_( PaUtil_InitializeBufferProcessor( &stream->bufferProcessor,
 
1815
                inputChannelCount, inputSampleFormat, hostInputSampleFormat,
 
1816
                outputChannelCount, outputSampleFormat, hostOutputSampleFormat,
 
1817
                sampleRate, streamFlags,
 
1818
                framesPerBuffer, framesPerHostBuffer, paUtilFixedHostBufferSize,
 
1819
                streamCallback, userData ) );
 
1820
 
 
1821
    stream->baseStreamRep.streamInfo.structVersion = 1;
 
1822
    stream->baseStreamRep.streamInfo.sampleRate = sampleRate;
 
1823
    /* Determine input latency from buffer processor and buffer sizes */
 
1824
    if( stream->input )
 
1825
    {
 
1826
        PaTime bufferDuration = ( stream->input->hostBufferSize + stream->input->hardwareBufferSize )
 
1827
                                / sampleRate / stream->input->bytesPerFrame;
 
1828
        stream->baseStreamRep.streamInfo.inputLatency =
 
1829
            PaUtil_GetBufferProcessorInputLatency( &stream->bufferProcessor ) +
 
1830
            bufferDuration - stream->maxFramesPerHostBuffer / sampleRate;
 
1831
        assert( stream->baseStreamRep.streamInfo.inputLatency > 0.0 );
 
1832
    }
 
1833
    /* Determine output latency from buffer processor and buffer sizes */
 
1834
    if( stream->output )
 
1835
    {
 
1836
        PaTime bufferDuration = ( stream->output->hostBufferSize + stream->output->hardwareBufferSize )
 
1837
                                / sampleRate / stream->output->bytesPerFrame;
 
1838
        /* Take buffer size cap into account (see PaAsiHpi_WaitForFrames) */
 
1839
        if( !stream->input && (stream->output->outputBufferCap > 0) )
 
1840
        {
 
1841
            bufferDuration = PA_MIN( bufferDuration,
 
1842
                                     stream->output->outputBufferCap / sampleRate / stream->output->bytesPerFrame );
 
1843
        }
 
1844
        stream->baseStreamRep.streamInfo.outputLatency =
 
1845
            PaUtil_GetBufferProcessorOutputLatency( &stream->bufferProcessor ) +
 
1846
            bufferDuration - stream->maxFramesPerHostBuffer / sampleRate;
 
1847
        assert( stream->baseStreamRep.streamInfo.outputLatency > 0.0 );
 
1848
    }
 
1849
 
 
1850
    /* Report stream info, for debugging purposes */
 
1851
    PaAsiHpi_StreamDump( stream );
 
1852
 
 
1853
    /* Save initialized stream to PA stream list */
 
1854
    *s = (PaStream*)stream;
 
1855
    return result;
 
1856
 
 
1857
error:
 
1858
    CloseStream( (PaStream*)stream );
 
1859
    return result;
 
1860
}
 
1861
 
 
1862
 
 
1863
/** Close PortAudio stream.
 
1864
 When CloseStream() is called, the multi-api layer ensures that the stream has already
 
1865
 been stopped or aborted. This closes the underlying HPI streams and deallocates stream
 
1866
 buffers and structs.
 
1867
 
 
1868
 @param s Pointer to PortAudio stream
 
1869
 
 
1870
 @return PortAudio error code
 
1871
*/
 
1872
static PaError CloseStream( PaStream *s )
 
1873
{
 
1874
    PaError result = paNoError;
 
1875
    PaAsiHpiStream *stream = (PaAsiHpiStream*)s;
 
1876
 
 
1877
    /* If stream is already gone, all is well */
 
1878
    if( stream == NULL )
 
1879
        return paNoError;
 
1880
 
 
1881
    /* Generic stream cleanup */
 
1882
    PaUtil_TerminateBufferProcessor( &stream->bufferProcessor );
 
1883
    PaUtil_TerminateStreamRepresentation( &stream->baseStreamRep );
 
1884
 
 
1885
    /* Implementation-specific details - close internal streams */
 
1886
    if( stream->input )
 
1887
    {
 
1888
        /* Close HPI stream (freeing BBM host buffer in the process, if used) */
 
1889
        if( stream->input->hpiStream )
 
1890
        {
 
1891
            PA_ASIHPI_UNLESS_( HPI_InStreamClose( stream->input->hpiDevice->subSys,
 
1892
                                                  stream->input->hpiStream ), paUnanticipatedHostError );
 
1893
        }
 
1894
        /* Free temp buffer and stream component */
 
1895
        PaUtil_FreeMemory( stream->input->tempBuffer );
 
1896
        PaUtil_FreeMemory( stream->input );
 
1897
    }
 
1898
    if( stream->output )
 
1899
    {
 
1900
        /* Close HPI stream (freeing BBM host buffer in the process, if used) */
 
1901
        if( stream->output->hpiStream )
 
1902
        {
 
1903
            PA_ASIHPI_UNLESS_( HPI_OutStreamClose( stream->output->hpiDevice->subSys,
 
1904
                                                   stream->output->hpiStream ), paUnanticipatedHostError );
 
1905
        }
 
1906
        /* Free temp buffer and stream component */
 
1907
        PaUtil_FreeMemory( stream->output->tempBuffer );
 
1908
        PaUtil_FreeMemory( stream->output );
 
1909
    }
 
1910
 
 
1911
    PaUtil_FreeMemory( stream->blockingUserBufferCopy );
 
1912
    PaUtil_FreeMemory( stream );
 
1913
 
 
1914
error:
 
1915
    return result;
 
1916
}
 
1917
 
 
1918
 
 
1919
/** Prime HPI output stream with silence.
 
1920
 This resets the output stream and uses PortAudio helper routines to fill the
 
1921
 temp buffer with silence. It then writes two host buffers to the stream. This is supposed
 
1922
 to be called before the stream is started. It has no effect on input-only streams.
 
1923
 
 
1924
 @param stream Pointer to stream struct
 
1925
 
 
1926
 @return PortAudio error code
 
1927
 */
 
1928
static PaError PaAsiHpi_PrimeOutputWithSilence( PaAsiHpiStream *stream )
 
1929
{
 
1930
    PaError result = paNoError;
 
1931
    PaAsiHpiStreamComponent *out;
 
1932
    PaUtilZeroer *zeroer;
 
1933
    PaSampleFormat outputFormat;
 
1934
#if (HPI_VER < HPI_VERSION_CONSTRUCTOR( 3, 5, 5 ))
 
1935
    HPI_DATA data;
 
1936
#endif
 
1937
    assert( stream );
 
1938
    out = stream->output;
 
1939
    /* Only continue if stream has output channels */
 
1940
    if( !out )
 
1941
        return result;
 
1942
    assert( out->tempBuffer );
 
1943
 
 
1944
    /* Clear all existing data in hardware playback buffer */
 
1945
    PA_ASIHPI_UNLESS_( HPI_OutStreamReset( out->hpiDevice->subSys,
 
1946
                                           out->hpiStream ), paUnanticipatedHostError );
 
1947
    /* Fill temp buffer with silence */
 
1948
    outputFormat = PaAsiHpi_HpiToPaFormat( out->hpiFormat.wFormat );
 
1949
    zeroer = PaUtil_SelectZeroer( outputFormat );
 
1950
    zeroer(out->tempBuffer, 1, out->tempBufferSize / Pa_GetSampleSize(outputFormat) );
 
1951
    /* Write temp buffer to hardware fifo twice, to get started */
 
1952
#if (HPI_VER >= HPI_VERSION_CONSTRUCTOR( 3, 5, 5 ))
 
1953
    PA_ASIHPI_UNLESS_( HPI_OutStreamWriteBuf( out->hpiDevice->subSys, out->hpiStream, 
 
1954
                                              out->tempBuffer, out->tempBufferSize, &out->hpiFormat),
 
1955
                                              paUnanticipatedHostError );
 
1956
    PA_ASIHPI_UNLESS_( HPI_OutStreamWriteBuf( out->hpiDevice->subSys, out->hpiStream, 
 
1957
                                              out->tempBuffer, out->tempBufferSize, &out->hpiFormat),
 
1958
                                              paUnanticipatedHostError );
 
1959
#else
 
1960
    PA_ASIHPI_UNLESS_( HPI_DataCreate( &data, &out->hpiFormat, out->tempBuffer, out->tempBufferSize ),
 
1961
                       paUnanticipatedHostError );
 
1962
    PA_ASIHPI_UNLESS_( HPI_OutStreamWrite( out->hpiDevice->subSys,
 
1963
                                           out->hpiStream, &data ), paUnanticipatedHostError );
 
1964
    PA_ASIHPI_UNLESS_( HPI_OutStreamWrite( out->hpiDevice->subSys,
 
1965
                                           out->hpiStream, &data ), paUnanticipatedHostError );
 
1966
#endif
 
1967
error:
 
1968
    return result;
 
1969
}
 
1970
 
 
1971
 
 
1972
/** Start HPI streams (both input + output).
 
1973
 This starts all HPI streams in the PortAudio stream. Output streams are first primed with
 
1974
 silence, if required. After this call the PA stream is in the Active state.
 
1975
 
 
1976
 @todo Implement priming via the user callback
 
1977
 
 
1978
 @param stream Pointer to stream struct
 
1979
 
 
1980
 @param outputPrimed True if output is already primed (if false, silence will be loaded before starting)
 
1981
 
 
1982
 @return PortAudio error code
 
1983
 */
 
1984
static PaError PaAsiHpi_StartStream( PaAsiHpiStream *stream, int outputPrimed )
 
1985
{
 
1986
    PaError result = paNoError;
 
1987
 
 
1988
    if( stream->input )
 
1989
    {
 
1990
        PA_ASIHPI_UNLESS_( HPI_InStreamStart( stream->input->hpiDevice->subSys,
 
1991
                                              stream->input->hpiStream ), paUnanticipatedHostError );
 
1992
    }
 
1993
    if( stream->output )
 
1994
    {
 
1995
        if( !outputPrimed )
 
1996
        {
 
1997
            /* Buffer isn't primed, so load stream with silence */
 
1998
            PA_ENSURE_( PaAsiHpi_PrimeOutputWithSilence( stream ) );
 
1999
        }
 
2000
        PA_ASIHPI_UNLESS_( HPI_OutStreamStart( stream->output->hpiDevice->subSys,
 
2001
                                               stream->output->hpiStream ), paUnanticipatedHostError );
 
2002
    }
 
2003
    stream->state = paAsiHpiActiveState;
 
2004
    stream->callbackFinished = 0;
 
2005
 
 
2006
    /* Report stream info for debugging purposes */
 
2007
    /*    PaAsiHpi_StreamDump( stream );   */
 
2008
 
 
2009
error:
 
2010
    return result;
 
2011
}
 
2012
 
 
2013
 
 
2014
/** Start PortAudio stream.
 
2015
 If the stream has a callback interface, this starts a helper thread to feed the user callback.
 
2016
 The thread will then take care of starting the HPI streams, and this function will block
 
2017
 until the streams actually start. In the case of a blocking interface, the HPI streams
 
2018
 are simply started.
 
2019
 
 
2020
 @param s Pointer to PortAudio stream
 
2021
 
 
2022
 @return PortAudio error code
 
2023
*/
 
2024
static PaError StartStream( PaStream *s )
 
2025
{
 
2026
    PaError result = paNoError;
 
2027
    PaAsiHpiStream *stream = (PaAsiHpiStream*)s;
 
2028
 
 
2029
    assert( stream );
 
2030
 
 
2031
    /* Ready the processor */
 
2032
    PaUtil_ResetBufferProcessor( &stream->bufferProcessor );
 
2033
 
 
2034
    if( stream->callbackMode )
 
2035
    {
 
2036
        /* Create and start callback engine thread */
 
2037
        /* Also waits 1 second for stream to be started by engine thread (otherwise aborts) */
 
2038
        PA_ENSURE_( PaUnixThread_New( &stream->thread, &CallbackThreadFunc, stream, 1., 0 /*rtSched*/ ) );
 
2039
    }
 
2040
    else
 
2041
    {
 
2042
        PA_ENSURE_( PaAsiHpi_StartStream( stream, 0 ) );
 
2043
    }
 
2044
 
 
2045
error:
 
2046
    return result;
 
2047
}
 
2048
 
 
2049
 
 
2050
/** Stop HPI streams (input + output), either softly or abruptly.
 
2051
 If abort is false, the function blocks until the output stream is drained, otherwise it
 
2052
 stops immediately and discards data in the stream hardware buffers.
 
2053
 
 
2054
 This function is safe to call from the callback engine thread as well as the main thread.
 
2055
 
 
2056
 @param stream Pointer to stream struct
 
2057
 
 
2058
 @param abort True if samples in output buffer should be discarded (otherwise blocks until stream is done)
 
2059
 
 
2060
 @return PortAudio error code
 
2061
 
 
2062
 */
 
2063
static PaError PaAsiHpi_StopStream( PaAsiHpiStream *stream, int abort )
 
2064
{
 
2065
    PaError result = paNoError;
 
2066
 
 
2067
    assert( stream );
 
2068
 
 
2069
    /* Input channels */
 
2070
    if( stream->input )
 
2071
    {
 
2072
        PA_ASIHPI_UNLESS_( HPI_InStreamReset( stream->input->hpiDevice->subSys,
 
2073
                                              stream->input->hpiStream ), paUnanticipatedHostError );
 
2074
    }
 
2075
    /* Output channels */
 
2076
    if( stream->output )
 
2077
    {
 
2078
        if( !abort )
 
2079
        {
 
2080
            /* Wait until HPI output stream is drained */
 
2081
            while( 1 )
 
2082
            {
 
2083
                PaAsiHpiStreamInfo streamInfo;
 
2084
                PaTime timeLeft;
 
2085
 
 
2086
                /* Obtain number of samples waiting to be played */
 
2087
                PA_ENSURE_( PaAsiHpi_GetStreamInfo( stream->output, &streamInfo ) );
 
2088
                /* Check if stream is drained */
 
2089
                if( (streamInfo.state != HPI_STATE_PLAYING) &&
 
2090
                        (streamInfo.dataSize < stream->output->bytesPerFrame * PA_ASIHPI_MIN_FRAMES_) )
 
2091
                    break;
 
2092
                /* Sleep amount of time represented by remaining samples */
 
2093
                timeLeft = 1000.0 * streamInfo.dataSize / stream->output->bytesPerFrame
 
2094
                           / stream->baseStreamRep.streamInfo.sampleRate;
 
2095
                Pa_Sleep( (long)ceil( timeLeft ) );
 
2096
            }
 
2097
        }
 
2098
        PA_ASIHPI_UNLESS_( HPI_OutStreamReset( stream->output->hpiDevice->subSys,
 
2099
                                               stream->output->hpiStream ), paUnanticipatedHostError );
 
2100
    }
 
2101
 
 
2102
    /* Report stream info for debugging purposes */
 
2103
    /*    PaAsiHpi_StreamDump( stream ); */
 
2104
 
 
2105
error:
 
2106
    return result;
 
2107
}
 
2108
 
 
2109
 
 
2110
/** Stop or abort PortAudio stream.
 
2111
 
 
2112
 This function is used to explicitly stop the PortAudio stream (via StopStream/AbortStream),
 
2113
 as opposed to the situation when the callback finishes with a result other than paContinue.
 
2114
 If a stream is in callback mode we will have to inspect whether the background thread has
 
2115
 finished, or we will have to take it out. In either case we join the thread before returning.
 
2116
 In blocking mode, we simply tell HPI to stop abruptly (abort) or finish buffers (drain).
 
2117
 The PortAudio stream will be in the Stopped state after a call to this function.
 
2118
 
 
2119
 Don't call this from the callback engine thread!
 
2120
 
 
2121
 @param stream Pointer to stream struct
 
2122
 
 
2123
 @param abort True if samples in output buffer should be discarded (otherwise blocks until stream is done)
 
2124
 
 
2125
 @return PortAudio error code
 
2126
*/
 
2127
static PaError PaAsiHpi_ExplicitStop( PaAsiHpiStream *stream, int abort )
 
2128
{
 
2129
    PaError result = paNoError;
 
2130
 
 
2131
    /* First deal with the callback thread, cancelling and/or joining it if necessary */
 
2132
    if( stream->callbackMode )
 
2133
    {
 
2134
        PaError threadRes;
 
2135
        stream->callbackAbort = abort;
 
2136
        if( abort )
 
2137
        {
 
2138
            PA_DEBUG(( "Aborting callback\n" ));
 
2139
        }
 
2140
        else
 
2141
        {
 
2142
            PA_DEBUG(( "Stopping callback\n" ));
 
2143
        }
 
2144
        PA_ENSURE_( PaUnixThread_Terminate( &stream->thread, !abort, &threadRes ) );
 
2145
        if( threadRes != paNoError )
 
2146
        {
 
2147
            PA_DEBUG(( "Callback thread returned: %d\n", threadRes ));
 
2148
        }
 
2149
    }
 
2150
    else
 
2151
    {
 
2152
        PA_ENSURE_( PaAsiHpi_StopStream( stream, abort ) );
 
2153
    }
 
2154
 
 
2155
    stream->state = paAsiHpiStoppedState;
 
2156
 
 
2157
error:
 
2158
    return result;
 
2159
}
 
2160
 
 
2161
 
 
2162
/** Stop PortAudio stream.
 
2163
 This blocks until the output buffers are drained.
 
2164
 
 
2165
 @param s Pointer to PortAudio stream
 
2166
 
 
2167
 @return PortAudio error code
 
2168
*/
 
2169
static PaError StopStream( PaStream *s )
 
2170
{
 
2171
    return PaAsiHpi_ExplicitStop( (PaAsiHpiStream *) s, 0 );
 
2172
}
 
2173
 
 
2174
 
 
2175
/** Abort PortAudio stream.
 
2176
 This discards any existing data in output buffers and stops the stream immediately.
 
2177
 
 
2178
 @param s Pointer to PortAudio stream
 
2179
 
 
2180
 @return PortAudio error code
 
2181
*/
 
2182
static PaError AbortStream( PaStream *s )
 
2183
{
 
2184
    return PaAsiHpi_ExplicitStop( (PaAsiHpiStream * ) s, 1 );
 
2185
}
 
2186
 
 
2187
 
 
2188
/** Determine whether the stream is stopped.
 
2189
 A stream is considered to be stopped prior to a successful call to StartStream and after
 
2190
 a successful call to StopStream or AbortStream. If a stream callback returns a value other
 
2191
 than paContinue the stream is NOT considered to be stopped (it is in CallbackFinished state).
 
2192
 
 
2193
 @param s Pointer to PortAudio stream
 
2194
 
 
2195
 @return Returns one (1) when the stream is stopped, zero (0) when the stream is running, or
 
2196
         a PaErrorCode (which are always negative) if PortAudio is not initialized or an
 
2197
         error is encountered.
 
2198
*/
 
2199
static PaError IsStreamStopped( PaStream *s )
 
2200
{
 
2201
    PaAsiHpiStream *stream = (PaAsiHpiStream*)s;
 
2202
 
 
2203
    assert( stream );
 
2204
    return stream->state == paAsiHpiStoppedState ? 1 : 0;
 
2205
}
 
2206
 
 
2207
 
 
2208
/** Determine whether the stream is active.
 
2209
 A stream is active after a successful call to StartStream(), until it becomes inactive either
 
2210
 as a result of a call to StopStream() or AbortStream(), or as a result of a return value
 
2211
 other than paContinue from the stream callback. In the latter case, the stream is considered
 
2212
 inactive after the last buffer has finished playing.
 
2213
 
 
2214
 @param s Pointer to PortAudio stream
 
2215
 
 
2216
 @return Returns one (1) when the stream is active (i.e. playing or recording audio),
 
2217
         zero (0) when not playing, or a PaErrorCode (which are always negative)
 
2218
         if PortAudio is not initialized or an error is encountered.
 
2219
*/
 
2220
static PaError IsStreamActive( PaStream *s )
 
2221
{
 
2222
    PaAsiHpiStream *stream = (PaAsiHpiStream*)s;
 
2223
 
 
2224
    assert( stream );
 
2225
    return stream->state == paAsiHpiActiveState ? 1 : 0;
 
2226
}
 
2227
 
 
2228
 
 
2229
/** Returns current stream time.
 
2230
 This corresponds to the system clock. The clock should run continuously while the stream
 
2231
 is open, i.e. between calls to OpenStream() and CloseStream(), therefore a frame counter
 
2232
 is not good enough.
 
2233
 
 
2234
 @param s Pointer to PortAudio stream
 
2235
 
 
2236
 @return Stream time, in seconds
 
2237
 */
 
2238
static PaTime GetStreamTime( PaStream *s )
 
2239
{
 
2240
    return PaUtil_GetTime();
 
2241
}
 
2242
 
 
2243
 
 
2244
/** Returns CPU load.
 
2245
 
 
2246
 @param s Pointer to PortAudio stream
 
2247
 
 
2248
 @return CPU load (0.0 if blocking interface is used)
 
2249
 */
 
2250
static double GetStreamCpuLoad( PaStream *s )
 
2251
{
 
2252
    PaAsiHpiStream *stream = (PaAsiHpiStream*)s;
 
2253
 
 
2254
    return stream->callbackMode ? PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer ) : 0.0;
 
2255
}
 
2256
 
 
2257
/* --------------------------- Callback Interface --------------------------- */
 
2258
 
 
2259
/** Exit routine which is called when callback thread quits.
 
2260
 This takes care of stopping the HPI streams (either waiting for output to finish, or
 
2261
 abruptly). It also calls the user-supplied StreamFinished callback, and sets the
 
2262
 stream state to CallbackFinished if it was reached via a non-paContinue return from
 
2263
 the user callback function.
 
2264
 
 
2265
 @param userData A pointer to an open stream previously created with Pa_OpenStream
 
2266
 */
 
2267
static void PaAsiHpi_OnThreadExit( void *userData )
 
2268
{
 
2269
    PaAsiHpiStream *stream = (PaAsiHpiStream *) userData;
 
2270
 
 
2271
    assert( stream );
 
2272
 
 
2273
    PaUtil_ResetCpuLoadMeasurer( &stream->cpuLoadMeasurer );
 
2274
 
 
2275
    PA_DEBUG(( "%s: Stopping HPI streams\n", __FUNCTION__ ));
 
2276
    PaAsiHpi_StopStream( stream, stream->callbackAbort );
 
2277
    PA_DEBUG(( "%s: Stoppage\n", __FUNCTION__ ));
 
2278
 
 
2279
    /* Eventually notify user all buffers have played */
 
2280
    if( stream->baseStreamRep.streamFinishedCallback )
 
2281
    {
 
2282
        stream->baseStreamRep.streamFinishedCallback( stream->baseStreamRep.userData );
 
2283
    }
 
2284
 
 
2285
    /* Unfortunately both explicit calls to Stop/AbortStream (leading to Stopped state)
 
2286
     and implicit stops via paComplete/paAbort (leading to CallbackFinished state)
 
2287
     end up here - need another flag to remind us which is the case */
 
2288
    if( stream->callbackFinished )
 
2289
        stream->state = paAsiHpiCallbackFinishedState;
 
2290
}
 
2291
 
 
2292
 
 
2293
/** Wait until there is enough frames to fill a host buffer.
 
2294
 The routine attempts to sleep until at least a full host buffer can be retrieved from the
 
2295
 input HPI stream and passed to the output HPI stream. It will first sleep until enough
 
2296
 output space is available, as this is usually easily achievable. If it is an output-only
 
2297
 stream, it will also sleep if the hardware buffer is too full, thereby throttling the
 
2298
 filling of the output buffer and reducing output latency. The routine then blocks until
 
2299
 enough input samples are available, unless this will cause an output underflow. In the
 
2300
 process, input overflows and output underflows are indicated.
 
2301
 
 
2302
 @param stream Pointer to stream struct
 
2303
 
 
2304
 @param framesAvail Returns the number of available frames
 
2305
 
 
2306
 @param cbFlags Overflows and underflows indicated in here
 
2307
 
 
2308
 @return PortAudio error code (only paUnanticipatedHostError expected)
 
2309
 */
 
2310
static PaError PaAsiHpi_WaitForFrames( PaAsiHpiStream *stream, unsigned long *framesAvail,
 
2311
                                       PaStreamCallbackFlags *cbFlags )
 
2312
{
 
2313
    PaError result = paNoError;
 
2314
    double sampleRate;
 
2315
    unsigned long framesTarget;
 
2316
    HW32 outputData = 0, outputSpace = 0, inputData = 0, framesLeft = 0;
 
2317
 
 
2318
    assert( stream );
 
2319
    assert( stream->input || stream->output );
 
2320
 
 
2321
    sampleRate = stream->baseStreamRep.streamInfo.sampleRate;
 
2322
    /* We have to come up with this much frames on both input and output */
 
2323
    framesTarget = stream->bufferProcessor.framesPerHostBuffer;
 
2324
    assert( framesTarget > 0 );
 
2325
 
 
2326
    while( 1 )
 
2327
    {
 
2328
        PaAsiHpiStreamInfo info;
 
2329
        /* Check output first, as this takes priority in the default full-duplex mode */
 
2330
        if( stream->output )
 
2331
        {
 
2332
            PA_ENSURE_( PaAsiHpi_GetStreamInfo( stream->output, &info ) );
 
2333
            /* Wait until enough space is available in output buffer to receive a full block */
 
2334
            if( info.availableFrames < framesTarget )
 
2335
            {
 
2336
                framesLeft = framesTarget - info.availableFrames;
 
2337
                Pa_Sleep( (long)ceil( 1000 * framesLeft / sampleRate ) );
 
2338
                continue;
 
2339
            }
 
2340
            /* Wait until the data in hardware buffer has dropped to a sensible level.
 
2341
             Without this, the hardware buffer quickly fills up in the absence of an input
 
2342
             stream to regulate its data rate (if data generation is fast). This leads to
 
2343
             large latencies, as the AudioScience hardware buffers are humongous.
 
2344
             This is similar to the default "Hardware Buffering=off" option in the
 
2345
             AudioScience WAV driver. */
 
2346
            if( !stream->input && (stream->output->outputBufferCap > 0) &&
 
2347
                    ( info.totalBufferedData > stream->output->outputBufferCap / stream->output->bytesPerFrame ) )
 
2348
            {
 
2349
                framesLeft = info.totalBufferedData - stream->output->outputBufferCap / stream->output->bytesPerFrame;
 
2350
                Pa_Sleep( (long)ceil( 1000 * framesLeft / sampleRate ) );
 
2351
                continue;
 
2352
            }
 
2353
            outputData = info.totalBufferedData;
 
2354
            outputSpace = info.availableFrames;
 
2355
            /* Report output underflow to callback */
 
2356
            if( info.underflow )
 
2357
            {
 
2358
                *cbFlags |= paOutputUnderflow;
 
2359
            }
 
2360
        }
 
2361
 
 
2362
        /* Now check input side */
 
2363
        if( stream->input )
 
2364
        {
 
2365
            PA_ENSURE_( PaAsiHpi_GetStreamInfo( stream->input, &info ) );
 
2366
            /* If a full block of samples hasn't been recorded yet, wait for it if possible */
 
2367
            if( info.availableFrames < framesTarget )
 
2368
            {
 
2369
                framesLeft = framesTarget - info.availableFrames;
 
2370
                /* As long as output is not disrupted in the process, wait for a full
 
2371
                block of input samples */
 
2372
                if( !stream->output || (outputData > framesLeft) )
 
2373
                {
 
2374
                    Pa_Sleep( (long)ceil( 1000 * framesLeft / sampleRate ) );
 
2375
                    continue;
 
2376
                }
 
2377
            }
 
2378
            inputData = info.availableFrames;
 
2379
            /** @todo The paInputOverflow flag should be set in the callback containing the
 
2380
             first input sample following the overflow. That means the block currently sitting
 
2381
             at the fore-front of recording, i.e. typically the one containing the newest (last)
 
2382
             sample in the HPI buffer system. This is most likely not the same as the current
 
2383
             block of data being passed to the callback. The current overflow should ideally
 
2384
             be noted in an overflow list of sorts, with an indication of when it should be
 
2385
             reported. The trouble starts if there are several separate overflow incidents,
 
2386
             given a big input buffer. Oh well, something to try out later... */
 
2387
            if( info.overflow )
 
2388
            {
 
2389
                *cbFlags |= paInputOverflow;
 
2390
            }
 
2391
        }
 
2392
        break;
 
2393
    }
 
2394
    /* Full-duplex stream */
 
2395
    if( stream->input && stream->output )
 
2396
    {
 
2397
        if( outputSpace >= framesTarget )
 
2398
            *framesAvail = outputSpace;
 
2399
        /* If input didn't make the target, keep the output count instead (input underflow) */
 
2400
        if( (inputData >= framesTarget) && (inputData < outputSpace) )
 
2401
            *framesAvail = inputData;
 
2402
    }
 
2403
    else
 
2404
    {
 
2405
        *framesAvail = stream->input ? inputData : outputSpace;
 
2406
    }
 
2407
 
 
2408
error:
 
2409
    return result;
 
2410
}
 
2411
 
 
2412
 
 
2413
/** Obtain recording, current and playback timestamps of stream.
 
2414
 The current time is determined by the system clock. This "now" timestamp occurs at the
 
2415
 forefront of recording (and playback in the full-duplex case), which happens later than the
 
2416
 input timestamp by an amount equal to the total number of recorded frames in the input buffer.
 
2417
 The output timestamp indicates when the next generated sample will actually be played. This
 
2418
 happens after all the samples currently in the output buffer are played. The output timestamp
 
2419
 therefore follows the current timestamp by an amount equal to the number of frames yet to be
 
2420
 played back in the output buffer.
 
2421
 
 
2422
 If the current timestamp is the present, the input timestamp is in the past and the output
 
2423
 timestamp is in the future.
 
2424
 
 
2425
 @param stream Pointer to stream struct
 
2426
 
 
2427
 @param timeInfo Pointer to timeInfo struct that will contain timestamps
 
2428
 */
 
2429
static void PaAsiHpi_CalculateTimeInfo( PaAsiHpiStream *stream, PaStreamCallbackTimeInfo *timeInfo )
 
2430
{
 
2431
    PaAsiHpiStreamInfo streamInfo;
 
2432
    double sampleRate;
 
2433
 
 
2434
    assert( stream );
 
2435
    assert( timeInfo );
 
2436
    sampleRate = stream->baseStreamRep.streamInfo.sampleRate;
 
2437
 
 
2438
    /* The current time ("now") is at the forefront of both recording and playback */
 
2439
    timeInfo->currentTime = GetStreamTime( (PaStream *)stream );
 
2440
    /* The last sample in the input buffer was recorded just now, so the first sample
 
2441
     happened (number of recorded samples)/sampleRate ago */
 
2442
    timeInfo->inputBufferAdcTime = timeInfo->currentTime;
 
2443
    if( stream->input )
 
2444
    {
 
2445
        PaAsiHpi_GetStreamInfo( stream->input, &streamInfo );
 
2446
        timeInfo->inputBufferAdcTime -= streamInfo.totalBufferedData / sampleRate;
 
2447
    }
 
2448
    /* The first of the outgoing samples will be played after all the samples in the output
 
2449
     buffer is done */
 
2450
    timeInfo->outputBufferDacTime = timeInfo->currentTime;
 
2451
    if( stream->output )
 
2452
    {
 
2453
        PaAsiHpi_GetStreamInfo( stream->output, &streamInfo );
 
2454
        timeInfo->outputBufferDacTime += streamInfo.totalBufferedData / sampleRate;
 
2455
    }
 
2456
}
 
2457
 
 
2458
 
 
2459
/** Read from HPI input stream and register buffers.
 
2460
 This reads data from the HPI input stream (if it exists) and registers the temp stream
 
2461
 buffers of both input and output streams with the buffer processor. In the process it also
 
2462
 handles input underflows in the full-duplex case.
 
2463
 
 
2464
 @param stream Pointer to stream struct
 
2465
 
 
2466
 @param numFrames On entrance the number of available frames, on exit the number of
 
2467
                  received frames
 
2468
 
 
2469
 @param cbFlags Indicates overflows and underflows
 
2470
 
 
2471
 @return PortAudio error code
 
2472
 */
 
2473
static PaError PaAsiHpi_BeginProcessing( PaAsiHpiStream *stream, unsigned long *numFrames,
 
2474
        PaStreamCallbackFlags *cbFlags )
 
2475
{
 
2476
    PaError result = paNoError;
 
2477
 
 
2478
    assert( stream );
 
2479
    if( *numFrames > stream->maxFramesPerHostBuffer )
 
2480
        *numFrames = stream->maxFramesPerHostBuffer;
 
2481
 
 
2482
    if( stream->input )
 
2483
    {
 
2484
        PaAsiHpiStreamInfo info;
 
2485
        
 
2486
#if (HPI_VER < HPI_VERSION_CONSTRUCTOR( 3, 5, 5 ))
 
2487
        HPI_DATA data;
 
2488
#endif
 
2489
        HW32 framesToGet = *numFrames;
 
2490
 
 
2491
        /* Check for overflows and underflows yet again */
 
2492
        PA_ENSURE_( PaAsiHpi_GetStreamInfo( stream->input, &info ) );
 
2493
        if( info.overflow )
 
2494
        {
 
2495
            *cbFlags |= paInputOverflow;
 
2496
        }
 
2497
        /* Input underflow if less than expected number of samples pitch up */
 
2498
        if( framesToGet > info.availableFrames )
 
2499
        {
 
2500
            PaUtilZeroer *zeroer;
 
2501
            PaSampleFormat inputFormat;
 
2502
 
 
2503
            /* Never call an input-only stream with InputUnderflow set */
 
2504
            if( stream->output )
 
2505
                *cbFlags |= paInputUnderflow;
 
2506
            framesToGet = info.availableFrames;
 
2507
            /* Fill temp buffer with silence (to make up for missing input samples) */
 
2508
            inputFormat = PaAsiHpi_HpiToPaFormat( stream->input->hpiFormat.wFormat );
 
2509
            zeroer = PaUtil_SelectZeroer( inputFormat );
 
2510
            zeroer(stream->input->tempBuffer, 1,
 
2511
                   stream->input->tempBufferSize / Pa_GetSampleSize(inputFormat) );
 
2512
        }
 
2513
 
 
2514
#if (HPI_VER >= HPI_VERSION_CONSTRUCTOR( 3, 5, 5 ))
 
2515
        /* Read block of data into temp buffer */
 
2516
        PA_ASIHPI_UNLESS_( HPI_InStreamReadBuf( stream->input->hpiDevice->subSys,
 
2517
                                             stream->input->hpiStream, 
 
2518
                                             stream->input->tempBuffer,
 
2519
                                             framesToGet * stream->input->bytesPerFrame),
 
2520
                           paUnanticipatedHostError );
 
2521
#else
 
2522
        /* Setup HPI data structure around temp buffer */
 
2523
        HPI_DataCreate( &data, &stream->input->hpiFormat, stream->input->tempBuffer,
 
2524
                        framesToGet * stream->input->bytesPerFrame );
 
2525
        /* Read block of data into temp buffer */
 
2526
        PA_ASIHPI_UNLESS_( HPI_InStreamRead( stream->input->hpiDevice->subSys,
 
2527
                                             stream->input->hpiStream, &data ),
 
2528
                           paUnanticipatedHostError );
 
2529
#endif
 
2530
        /* Register temp buffer with buffer processor (always FULL buffer) */
 
2531
        PaUtil_SetInputFrameCount( &stream->bufferProcessor, *numFrames );
 
2532
        /* HPI interface only allows interleaved channels */
 
2533
        PaUtil_SetInterleavedInputChannels( &stream->bufferProcessor,
 
2534
                                            0, stream->input->tempBuffer,
 
2535
                                            stream->input->hpiFormat.wChannels );
 
2536
    }
 
2537
    if( stream->output )
 
2538
    {
 
2539
        /* Register temp buffer with buffer processor */
 
2540
        PaUtil_SetOutputFrameCount( &stream->bufferProcessor, *numFrames );
 
2541
        /* HPI interface only allows interleaved channels */
 
2542
        PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor,
 
2543
                                             0, stream->output->tempBuffer,
 
2544
                                             stream->output->hpiFormat.wChannels );
 
2545
    }
 
2546
 
 
2547
error:
 
2548
    return result;
 
2549
}
 
2550
 
 
2551
 
 
2552
/** Flush output buffers to HPI output stream.
 
2553
 This completes the processing cycle by writing the temp buffer to the HPI interface.
 
2554
 Additional output underflows are caught before data is written to the stream, as this
 
2555
 action typically remedies the underflow and hides it in the process.
 
2556
 
 
2557
 @param stream Pointer to stream struct
 
2558
 
 
2559
 @param numFrames The number of frames to write to the output stream
 
2560
 
 
2561
 @param cbFlags Indicates overflows and underflows
 
2562
 */
 
2563
static PaError PaAsiHpi_EndProcessing( PaAsiHpiStream *stream, unsigned long numFrames,
 
2564
                                       PaStreamCallbackFlags *cbFlags )
 
2565
{
 
2566
    PaError result = paNoError;
 
2567
 
 
2568
    assert( stream );
 
2569
 
 
2570
    if( stream->output )
 
2571
    {
 
2572
        PaAsiHpiStreamInfo info;
 
2573
#if (HPI_VER < HPI_VERSION_CONSTRUCTOR( 3, 5, 5 ))
 
2574
        HPI_DATA data;
 
2575
#endif
 
2576
        /* Check for underflows after the (potentially time-consuming) callback */
 
2577
        PA_ENSURE_( PaAsiHpi_GetStreamInfo( stream->output, &info ) );
 
2578
        if( info.underflow )
 
2579
        {
 
2580
            *cbFlags |= paOutputUnderflow;
 
2581
        }
 
2582
 
 
2583
#if (HPI_VER >= HPI_VERSION_CONSTRUCTOR( 3, 5, 5 ))
 
2584
        /* Write temp buffer to HPI stream */
 
2585
        PA_ASIHPI_UNLESS_( HPI_OutStreamWriteBuf( stream->output->hpiDevice->subSys,
 
2586
                                           stream->output->hpiStream, 
 
2587
                                           stream->output->tempBuffer,
 
2588
                                           numFrames * stream->output->bytesPerFrame, 
 
2589
                                           &stream->output->hpiFormat),
 
2590
                           paUnanticipatedHostError );
 
2591
#else
 
2592
        /* Setup HPI data structure around temp buffer */
 
2593
        HPI_DataCreate( &data, &stream->output->hpiFormat, stream->output->tempBuffer,
 
2594
                        numFrames * stream->output->bytesPerFrame );
 
2595
        /* Write temp buffer to HPI stream */
 
2596
        PA_ASIHPI_UNLESS_( HPI_OutStreamWrite( stream->output->hpiDevice->subSys,
 
2597
                                               stream->output->hpiStream, &data ),
 
2598
                           paUnanticipatedHostError );
 
2599
#endif
 
2600
    }
 
2601
 
 
2602
error:
 
2603
    return result;
 
2604
}
 
2605
 
 
2606
 
 
2607
/** Main callback engine.
 
2608
 This function runs in a separate thread and does all the work of fetching audio data from
 
2609
 the AudioScience card via the HPI interface, feeding it to the user callback via the buffer
 
2610
 processor, and delivering the resulting output data back to the card via HPI calls.
 
2611
 It is started and terminated when the PortAudio stream is started and stopped, and starts
 
2612
 the HPI streams on startup.
 
2613
 
 
2614
 @param userData A pointer to an open stream previously created with Pa_OpenStream.
 
2615
*/
 
2616
static void *CallbackThreadFunc( void *userData )
 
2617
{
 
2618
    PaError result = paNoError;
 
2619
    PaAsiHpiStream *stream = (PaAsiHpiStream *) userData;
 
2620
    int callbackResult = paContinue;
 
2621
 
 
2622
    assert( stream );
 
2623
 
 
2624
    /* Cleanup routine stops streams on thread exit */
 
2625
    pthread_cleanup_push( &PaAsiHpi_OnThreadExit, stream );
 
2626
 
 
2627
    /* Start HPI streams and notify parent when we're done */
 
2628
    PA_ENSURE_( PaUnixThread_PrepareNotify( &stream->thread ) );
 
2629
    /* Buffer will be primed with silence */
 
2630
    PA_ENSURE_( PaAsiHpi_StartStream( stream, 0 ) );
 
2631
    PA_ENSURE_( PaUnixThread_NotifyParent( &stream->thread ) );
 
2632
 
 
2633
    /* MAIN LOOP */
 
2634
    while( 1 )
 
2635
    {
 
2636
        PaStreamCallbackFlags cbFlags = 0;
 
2637
        unsigned long framesAvail, framesGot;
 
2638
 
 
2639
        pthread_testcancel();
 
2640
 
 
2641
        /** @concern StreamStop if the main thread has requested a stop and the stream has not
 
2642
        * been effectively stopped we signal this condition by modifying callbackResult
 
2643
        * (we'll want to flush buffered output). */
 
2644
        if( PaUnixThread_StopRequested( &stream->thread ) && (callbackResult == paContinue) )
 
2645
        {
 
2646
            PA_DEBUG(( "Setting callbackResult to paComplete\n" ));
 
2647
            callbackResult = paComplete;
 
2648
        }
 
2649
 
 
2650
        /* Start winding down thread if requested */
 
2651
        if( callbackResult != paContinue )
 
2652
        {
 
2653
            stream->callbackAbort = (callbackResult == paAbort);
 
2654
            if( stream->callbackAbort ||
 
2655
                    /** @concern BlockAdaption: Go on if adaption buffers are empty */
 
2656
                    PaUtil_IsBufferProcessorOutputEmpty( &stream->bufferProcessor ) )
 
2657
            {
 
2658
                goto end;
 
2659
            }
 
2660
            PA_DEBUG(( "%s: Flushing buffer processor\n", __FUNCTION__ ));
 
2661
            /* There is still buffered output that needs to be processed */
 
2662
        }
 
2663
 
 
2664
        /* SLEEP */
 
2665
        /* Wait for data (or buffer space) to become available. This basically sleeps and
 
2666
        polls the HPI interface until a full block of frames can be moved. */
 
2667
        PA_ENSURE_( PaAsiHpi_WaitForFrames( stream, &framesAvail, &cbFlags ) );
 
2668
 
 
2669
        /* Consume buffer space. Once we have a number of frames available for consumption we
 
2670
        must retrieve the data from the HPI interface and pass it to the PA buffer processor.
 
2671
        We should be prepared to process several chunks successively. */
 
2672
        while( framesAvail > 0 )
 
2673
        {
 
2674
            PaStreamCallbackTimeInfo timeInfo = {0, 0, 0};
 
2675
 
 
2676
            pthread_testcancel();
 
2677
 
 
2678
            framesGot = framesAvail;
 
2679
            if( stream->bufferProcessor.hostBufferSizeMode == paUtilFixedHostBufferSize )
 
2680
            {
 
2681
                /* We've committed to a fixed host buffer size, stick to that */
 
2682
                framesGot = framesGot >= stream->maxFramesPerHostBuffer ? stream->maxFramesPerHostBuffer : 0;
 
2683
            }
 
2684
            else
 
2685
            {
 
2686
                /* We've committed to an upper bound on the size of host buffers */
 
2687
                assert( stream->bufferProcessor.hostBufferSizeMode == paUtilBoundedHostBufferSize );
 
2688
                framesGot = PA_MIN( framesGot, stream->maxFramesPerHostBuffer );
 
2689
            }
 
2690
 
 
2691
            /* Obtain buffer timestamps */
 
2692
            PaAsiHpi_CalculateTimeInfo( stream, &timeInfo );
 
2693
            PaUtil_BeginBufferProcessing( &stream->bufferProcessor, &timeInfo, cbFlags );
 
2694
            /* CPU load measurement should include processing activivity external to the stream callback */
 
2695
            PaUtil_BeginCpuLoadMeasurement( &stream->cpuLoadMeasurer );
 
2696
            if( framesGot > 0 )
 
2697
            {
 
2698
                /* READ FROM HPI INPUT STREAM */
 
2699
                PA_ENSURE_( PaAsiHpi_BeginProcessing( stream, &framesGot, &cbFlags ) );
 
2700
                /* Input overflow in a full-duplex stream makes for interesting times */
 
2701
                if( stream->input && stream->output && (cbFlags & paInputOverflow) )
 
2702
                {
 
2703
                    /* Special full-duplex paNeverDropInput mode */
 
2704
                    if( stream->neverDropInput )
 
2705
                    {
 
2706
                        PaUtil_SetNoOutput( &stream->bufferProcessor );
 
2707
                        cbFlags |= paOutputOverflow;
 
2708
                    }
 
2709
                }
 
2710
                /* CALL USER CALLBACK WITH INPUT DATA, AND OBTAIN OUTPUT DATA */
 
2711
                PaUtil_EndBufferProcessing( &stream->bufferProcessor, &callbackResult );
 
2712
                /* Clear overflow and underflow information (but PaAsiHpi_EndProcessing might
 
2713
                still show up output underflow that will carry over to next round) */
 
2714
                cbFlags = 0;
 
2715
                /*  WRITE TO HPI OUTPUT STREAM */
 
2716
                PA_ENSURE_( PaAsiHpi_EndProcessing( stream, framesGot, &cbFlags ) );
 
2717
                /* Advance frame counter */
 
2718
                framesAvail -= framesGot;
 
2719
            }
 
2720
            PaUtil_EndCpuLoadMeasurement( &stream->cpuLoadMeasurer, framesGot );
 
2721
 
 
2722
            if( framesGot == 0 )
 
2723
            {
 
2724
                /* Go back to polling for more frames */
 
2725
                break;
 
2726
 
 
2727
            }
 
2728
            if( callbackResult != paContinue )
 
2729
                break;
 
2730
        }
 
2731
    }
 
2732
 
 
2733
    /* This code is unreachable, but important to include regardless because it
 
2734
     * is possibly a macro with a closing brace to match the opening brace in
 
2735
     * pthread_cleanup_push() above.  The documentation states that they must
 
2736
     * always occur in pairs. */
 
2737
    pthread_cleanup_pop( 1 );
 
2738
 
 
2739
end:
 
2740
    /* Indicates normal exit of callback, as opposed to the thread getting killed explicitly */
 
2741
    stream->callbackFinished = 1;
 
2742
    PA_DEBUG(( "%s: Thread %d exiting (callbackResult = %d)\n ",
 
2743
               __FUNCTION__, pthread_self(), callbackResult ));
 
2744
    /* Exit from thread and report any PortAudio error in the process */
 
2745
    PaUnixThreading_EXIT( result );
 
2746
error:
 
2747
    goto end;
 
2748
}
 
2749
 
 
2750
/* --------------------------- Blocking Interface --------------------------- */
 
2751
 
 
2752
/* As separate stream interfaces are used for blocking and callback streams, the following
 
2753
 functions can be guaranteed to only be called for blocking streams. */
 
2754
 
 
2755
/** Read data from input stream.
 
2756
 This reads the indicated number of frames into the supplied buffer from an input stream,
 
2757
 and blocks until this is done.
 
2758
 
 
2759
 @param s Pointer to PortAudio stream
 
2760
 
 
2761
 @param buffer Pointer to buffer that will receive interleaved data (or an array of pointers
 
2762
               to a buffer for each non-interleaved channel)
 
2763
 
 
2764
 @param frames Number of frames to read from stream
 
2765
 
 
2766
 @return PortAudio error code (also indicates overflow via paInputOverflowed)
 
2767
 */
 
2768
static PaError ReadStream( PaStream *s,
 
2769
                           void *buffer,
 
2770
                           unsigned long frames )
 
2771
{
 
2772
    PaError result = paNoError;
 
2773
    PaAsiHpiStream *stream = (PaAsiHpiStream*)s;
 
2774
    PaAsiHpiStreamInfo info;
 
2775
    void *userBuffer;
 
2776
 
 
2777
    assert( stream );
 
2778
    PA_UNLESS_( stream->input, paCanNotReadFromAnOutputOnlyStream );
 
2779
 
 
2780
    /* Check for input overflow since previous call to ReadStream */
 
2781
    PA_ENSURE_( PaAsiHpi_GetStreamInfo( stream->input, &info ) );
 
2782
    if( info.overflow )
 
2783
    {
 
2784
        result = paInputOverflowed;
 
2785
    }
 
2786
 
 
2787
    /* NB Make copy of user buffer pointers, since they are advanced by buffer processor */
 
2788
    if( stream->bufferProcessor.userInputIsInterleaved )
 
2789
    {
 
2790
        userBuffer = buffer;
 
2791
    }
 
2792
    else
 
2793
    {
 
2794
        /* Copy channels into local array */
 
2795
        userBuffer = stream->blockingUserBufferCopy;
 
2796
        memcpy( userBuffer, buffer, sizeof (void *) * stream->input->hpiFormat.wChannels );
 
2797
    }
 
2798
 
 
2799
    while( frames > 0 )
 
2800
    {
 
2801
        unsigned long framesGot, framesAvail;
 
2802
        PaStreamCallbackFlags cbFlags = 0;
 
2803
 
 
2804
        PA_ENSURE_( PaAsiHpi_WaitForFrames( stream, &framesAvail, &cbFlags ) );
 
2805
        framesGot = PA_MIN( framesAvail, frames );
 
2806
        PA_ENSURE_( PaAsiHpi_BeginProcessing( stream, &framesGot, &cbFlags ) );
 
2807
 
 
2808
        if( framesGot > 0 )
 
2809
        {
 
2810
            framesGot = PaUtil_CopyInput( &stream->bufferProcessor, &userBuffer, framesGot );
 
2811
            PA_ENSURE_( PaAsiHpi_EndProcessing( stream, framesGot, &cbFlags ) );
 
2812
            /* Advance frame counter */
 
2813
            frames -= framesGot;
 
2814
        }
 
2815
    }
 
2816
 
 
2817
error:
 
2818
    return result;
 
2819
}
 
2820
 
 
2821
 
 
2822
/** Write data to output stream.
 
2823
 This writes the indicated number of frames from the supplied buffer to an output stream,
 
2824
 and blocks until this is done.
 
2825
 
 
2826
 @param s Pointer to PortAudio stream
 
2827
 
 
2828
 @param buffer Pointer to buffer that provides interleaved data (or an array of pointers
 
2829
               to a buffer for each non-interleaved channel)
 
2830
 
 
2831
 @param frames Number of frames to write to stream
 
2832
 
 
2833
 @return PortAudio error code (also indicates underflow via paOutputUnderflowed)
 
2834
 */
 
2835
static PaError WriteStream( PaStream *s,
 
2836
                            const void *buffer,
 
2837
                            unsigned long frames )
 
2838
{
 
2839
    PaError result = paNoError;
 
2840
    PaAsiHpiStream *stream = (PaAsiHpiStream*)s;
 
2841
    PaAsiHpiStreamInfo info;
 
2842
    const void *userBuffer;
 
2843
 
 
2844
    assert( stream );
 
2845
    PA_UNLESS_( stream->output, paCanNotWriteToAnInputOnlyStream );
 
2846
 
 
2847
    /* Check for output underflow since previous call to WriteStream */
 
2848
    PA_ENSURE_( PaAsiHpi_GetStreamInfo( stream->output, &info ) );
 
2849
    if( info.underflow )
 
2850
    {
 
2851
        result = paOutputUnderflowed;
 
2852
    }
 
2853
 
 
2854
    /* NB Make copy of user buffer pointers, since they are advanced by buffer processor */
 
2855
    if( stream->bufferProcessor.userOutputIsInterleaved )
 
2856
    {
 
2857
        userBuffer = buffer;
 
2858
    }
 
2859
    else
 
2860
    {
 
2861
        /* Copy channels into local array */
 
2862
        userBuffer = stream->blockingUserBufferCopy;
 
2863
        memcpy( (void *)userBuffer, buffer, sizeof (void *) * stream->output->hpiFormat.wChannels );
 
2864
    }
 
2865
 
 
2866
    while( frames > 0 )
 
2867
    {
 
2868
        unsigned long framesGot, framesAvail;
 
2869
        PaStreamCallbackFlags cbFlags = 0;
 
2870
 
 
2871
        PA_ENSURE_( PaAsiHpi_WaitForFrames( stream, &framesAvail, &cbFlags ) );
 
2872
        framesGot = PA_MIN( framesAvail, frames );
 
2873
        PA_ENSURE_( PaAsiHpi_BeginProcessing( stream, &framesGot, &cbFlags ) );
 
2874
 
 
2875
        if( framesGot > 0 )
 
2876
        {
 
2877
            framesGot = PaUtil_CopyOutput( &stream->bufferProcessor, &userBuffer, framesGot );
 
2878
            PA_ENSURE_( PaAsiHpi_EndProcessing( stream, framesGot, &cbFlags ) );
 
2879
            /* Advance frame counter */
 
2880
            frames -= framesGot;
 
2881
        }
 
2882
    }
 
2883
 
 
2884
error:
 
2885
    return result;
 
2886
}
 
2887
 
 
2888
 
 
2889
/** Number of frames that can be read from input stream without blocking.
 
2890
 
 
2891
 @param s Pointer to PortAudio stream
 
2892
 
 
2893
 @return Number of frames, or PortAudio error code
 
2894
 */
 
2895
static signed long GetStreamReadAvailable( PaStream *s )
 
2896
{
 
2897
    PaError result = paNoError;
 
2898
    PaAsiHpiStream *stream = (PaAsiHpiStream*)s;
 
2899
    PaAsiHpiStreamInfo info;
 
2900
 
 
2901
    assert( stream );
 
2902
    PA_UNLESS_( stream->input, paCanNotReadFromAnOutputOnlyStream );
 
2903
 
 
2904
    PA_ENSURE_( PaAsiHpi_GetStreamInfo( stream->input, &info ) );
 
2905
    /* Round down to the nearest host buffer multiple */
 
2906
    result = (info.availableFrames / stream->maxFramesPerHostBuffer) * stream->maxFramesPerHostBuffer;
 
2907
    if( info.overflow )
 
2908
    {
 
2909
        result = paInputOverflowed;
 
2910
    }
 
2911
 
 
2912
error:
 
2913
    return result;
 
2914
}
 
2915
 
 
2916
 
 
2917
/** Number of frames that can be written to output stream without blocking.
 
2918
 
 
2919
 @param s Pointer to PortAudio stream
 
2920
 
 
2921
 @return Number of frames, or PortAudio error code
 
2922
 */
 
2923
static signed long GetStreamWriteAvailable( PaStream *s )
 
2924
{
 
2925
    PaError result = paNoError;
 
2926
    PaAsiHpiStream *stream = (PaAsiHpiStream*)s;
 
2927
    PaAsiHpiStreamInfo info;
 
2928
 
 
2929
    assert( stream );
 
2930
    PA_UNLESS_( stream->output, paCanNotWriteToAnInputOnlyStream );
 
2931
 
 
2932
    PA_ENSURE_( PaAsiHpi_GetStreamInfo( stream->output, &info ) );
 
2933
    /* Round down to the nearest host buffer multiple */
 
2934
    result = (info.availableFrames / stream->maxFramesPerHostBuffer) * stream->maxFramesPerHostBuffer;
 
2935
    if( info.underflow )
 
2936
    {
 
2937
        result = paOutputUnderflowed;
 
2938
    }
 
2939
 
 
2940
error:
 
2941
    return result;
 
2942
}