~ubuntu-branches/ubuntu/trusty/sflphone/trusty

« back to all changes in this revision

Viewing changes to daemon/libs/pjproject-2.0.1/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: (4.3.4 sid)
  • Revision ID: package-import@ubuntu.com-20140128182336-jrsv0k9u6cawc068
Tags: 1.3.0-1
* 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
 
}