2
* PortAudio Portable Real-Time Audio Library
3
* Latest Version at: http://www.portaudio.com
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>
8
* Copyright (c) 2003 Fred Gleason
9
* Copyright (c) 2005,2006 Ludwig Schwardt
11
* Permission is hereby granted, free of charge, to any person obtaining
12
* a copy of this software and associated documentation files
13
* (the "Software"), to deal in the Software without restriction,
14
* including without limitation the rights to use, copy, modify, merge,
15
* publish, distribute, sublicense, and/or sell copies of the Software,
16
* and to permit persons to whom the Software is furnished to do so,
17
* subject to the following conditions:
19
* The above copyright notice and this permission notice shall be
20
* included in all copies or substantial portions of the Software.
22
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
25
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
26
* ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
27
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
28
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
32
* The text above constitutes the entire PortAudio license; however,
33
* the PortAudio community also makes the following non-binding requests:
35
* Any person wishing to distribute modifications to the Software is
36
* requested to send the modifications to the original developer so that
37
* they can be incorporated into the canonical version. It is also
38
* requested that these non-binding requests be included along with the
43
* Modification History
44
* 12/2003 - Initial version
45
* 09/2005 - v19 version [rewrite]
50
@brief Host API implementation supporting AudioScience cards
51
via the Linux HPI interface.
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:
60
http://www.audioscience.com
62
Documentation for the HPI API can be found at:
64
http://www.audioscience.com/internet/download/sdk/spchpi.pdf
66
The Linux HPI driver itself (a kernel module + library) can be downloaded from:
68
http://www.audioscience.com/internet/download/linux_drivers.htm
70
<h3>Implementation strategy</h3>
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...
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
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.
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
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.
117
The HPI interface natively supports most common sample formats and sample rates (some
118
conversion is done on the adapter itself).
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.
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.
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.
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.
135
Output buffer priming via the user callback (i.e. paPrimeOutputBuffersUsingStreamCallback
136
and friends) is not implemented yet. All output is primed with silence.
138
Please send bug reports etc. to Ludwig Schwardt <schwardt@sun.ac.za>
144
#include <string.h> /* strlen() */
145
#include <pthread.h> /* pthreads and friends */
146
#include <assert.h> /* assert */
147
#include <math.h> /* ceil, floor */
149
#include <asihpi/hpi.h> /* HPI API */
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"
162
/* -------------------------------------------------------------------------- */
168
/* Error reporting and assertions */
170
/** Evaluate expression, and return on any PortAudio errors */
171
#define PA_ENSURE_(expr) \
173
PaError paError = (expr); \
174
if( UNLIKELY( paError < paNoError ) ) \
176
PA_DEBUG(( "Expression '" #expr "' failed in '" __FILE__ "', line: " STRINGIZE( __LINE__ ) "\n" )); \
182
/** Assert expression, else return the provided PaError */
183
#define PA_UNLESS_(expr, paError) \
185
if( UNLIKELY( (expr) == 0 ) ) \
187
PA_DEBUG(( "Expression '" #expr "' failed in '" __FILE__ "', line: " STRINGIZE( __LINE__ ) "\n" )); \
188
result = (paError); \
193
/** Check return value of HPI function, and map it to PaError */
194
#define PA_ASIHPI_UNLESS_(expr, paError) \
196
HW16 hpiError = (expr); \
197
/* If HPI error occurred */ \
198
if( UNLIKELY( hpiError ) ) \
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 ) \
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 ) ) \
211
PaUtil_SetLastHostErrorInfo( paInDevelopment, hpiError, szError ); \
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 ) \
218
result = (paError); \
224
/** Report HPI error code and text */
225
#define PA_ASIHPI_REPORT_ERROR_(hpiErrorCode) \
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 ) ) \
233
PaUtil_SetLastHostErrorInfo( paInDevelopment, (hpiErrorCode), szError ); \
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
249
/* -------------------------------------------------------------------------- */
255
/** Host API global data */
256
typedef struct PaAsiHpiHostApiRepresentation
258
/* PortAudio "base class" - keep the baseRep first! (C-style inheritance) */
259
PaUtilHostApiRepresentation baseHostApiRep;
260
PaUtilStreamInterface callbackStreamInterface;
261
PaUtilStreamInterface blockingStreamInterface;
263
PaUtilAllocationGroup *allocations;
265
/* implementation specific data goes here */
267
PaHostApiIndex hostApiIndex;
268
/** HPI subsystem pointer */
271
PaAsiHpiHostApiRepresentation;
275
typedef struct PaAsiHpiDeviceInfo
277
/* PortAudio "base class" - keep the baseRep first! (C-style inheritance) */
278
/** Common PortAudio device information */
279
PaDeviceInfo baseDeviceInfo;
281
/* implementation specific data goes here */
283
/** HPI subsystem (required for most HPI calls) */
287
/** Adapter model number (hex) */
289
/** Adapter HW/SW version */
291
/** Adapter serial number */
292
HW32 adapterSerialNumber;
295
/** 0=Input, 1=Output (HPI streams are either input or output but not both) */
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.
310
Here is a rough match-up:
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
319
paAsiHpiStoppedState=0,
320
paAsiHpiActiveState=1,
321
paAsiHpiCallbackFinishedState=2
326
/** Stream component data (associated with one direction, i.e. either input or output) */
327
typedef struct PaAsiHpiStreamComponent
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 */
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) */
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) */
350
/** Sample buffer size, in bytes */
353
PaAsiHpiStreamComponent;
357
typedef struct PaAsiHpiStream
359
/* PortAudio "base class" - keep the baseRep first! (C-style inheritance) */
360
PaUtilStreamRepresentation baseStreamRep;
361
PaUtilCpuLoadMeasurer cpuLoadMeasurer;
362
PaUtilBufferProcessor bufferProcessor;
364
PaUtilAllocationGroup *allocations;
366
/* implementation specific data goes here */
368
/** Separate structs for input and output sides of stream */
369
PaAsiHpiStreamComponent *input, *output;
371
/** Polling interval (in milliseconds) */
372
HW32 pollingInterval;
373
/** Are we running in callback mode? */
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 */
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;
385
/* Thread-related variables */
387
/** Helper thread which will deliver data to user callback */
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;
400
/** Stream state information, collected together for convenience */
401
typedef struct PaAsiHpiStreamInfo
403
/** HPI stream state (HPI_STATE_STOPPED, HPI_STATE_PLAYING, etc.) */
405
/** Size (in bytes) of recording/playback data buffer in HPI driver */
407
/** Amount of data (in bytes) available in the buffer */
409
/** Number of frames played/recorded since last stream reset */
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. */
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 */
423
/** Indicates that hardware buffer is getting too empty */
428
/* -------------------------------------------------------------------------- */
431
* Function prototypes
437
#endif /* __cplusplus */
439
/* The only exposed function in the entire host API implementation */
440
PaError PaAsiHpi_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
444
#endif /* __cplusplus */
446
static void Terminate( struct PaUtilHostApiRepresentation *hostApi );
447
static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
448
const PaStreamParameters *inputParameters,
449
const PaStreamParameters *outputParameters,
452
/* Stream prototypes */
453
static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
455
const PaStreamParameters *inputParameters,
456
const PaStreamParameters *outputParameters,
458
unsigned long framesPerBuffer,
459
PaStreamFlags streamFlags,
460
PaStreamCallback *streamCallback,
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 );
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 );
477
/* Callback prototypes */
478
static void *CallbackThreadFunc( void *userData );
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 );
511
/* ==========================================================================
512
* ============================= IMPLEMENTATION =============================
513
* ========================================================================== */
515
/* --------------------------- Host API Interface --------------------------- */
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
522
@param hpiHostApi Pointer to HPI host API struct
524
@return PortAudio error code (only paInsufficientMemory in practice)
526
static PaError PaAsiHpi_BuildDeviceList( PaAsiHpiHostApiRepresentation *hpiHostApi )
528
PaError result = paNoError;
529
PaUtilHostApiRepresentation *hostApi = &hpiHostApi->baseHostApiRep;
530
PaHostApiInfo *baseApiInfo = &hostApi->info;
531
PaAsiHpiDeviceInfo *hpiDeviceList;
532
HW16 adapterList[ HPI_MAX_ADAPTERS ];
535
int i, j, deviceCount = 0, deviceIndex = 0;
537
assert( hpiHostApi );
538
assert( hpiHostApi->subSys );
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 */
544
PA_ASIHPI_UNLESS_( HPI_SubSysFindAdapters( hpiHostApi->subSys, &numAdapters,
545
adapterList, HPI_MAX_ADAPTERS ), paNoError );
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 )
550
HW16 inStreams, outStreams;
555
/* If no adapter found at this index, skip it */
556
if( adapterList[i] == 0 )
559
/* Try to open adapter */
560
hpiError = HPI_AdapterOpen( hpiHostApi->subSys, i );
561
/* Report error and skip to next device on failure */
564
PA_ASIHPI_REPORT_ERROR_( hpiError );
567
hpiError = HPI_AdapterGetInfo( hpiHostApi->subSys, i,
568
&outStreams, &inStreams, &version, &serial, &type );
569
/* Skip to next device on failure */
572
PA_ASIHPI_REPORT_ERROR_( hpiError );
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;
587
/* Register any discovered devices */
588
if( deviceCount > 0 )
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 );
599
/* Now query devices again for information */
600
for( i=0; i < HPI_MAX_ADAPTERS; ++i )
602
HW16 inStreams, outStreams;
607
/* If no adapter found at this index, skip it */
608
if( adapterList[i] == 0 )
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 */
617
PA_ASIHPI_REPORT_ERROR_( hpiError );
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 */
630
/* First add all input streams as devices */
631
for( j=0; j < inStreams; ++j )
633
PaAsiHpiDeviceInfo *hpiDevice = &hpiDeviceList[deviceIndex];
634
PaDeviceInfo *baseDeviceInfo = &hpiDevice->baseDeviceInfo;
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 */
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;
669
/* Store device in global PortAudio list */
670
hostApi->deviceInfos[deviceIndex++] = (PaDeviceInfo *) hpiDevice;
673
/* Now add all output streams as devices (I know, the repetition is painful) */
674
for( j=0; j < outStreams; ++j )
676
PaAsiHpiDeviceInfo *hpiDevice = &hpiDeviceList[deviceIndex];
677
PaDeviceInfo *baseDeviceInfo = &hpiDevice->baseDeviceInfo;
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 */
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;
712
/* Store device in global PortAudio list */
713
hostApi->deviceInfos[deviceIndex++] = (PaDeviceInfo *) hpiDevice;
718
/* Finally acknowledge checked devices */
719
baseApiInfo->deviceCount = deviceIndex;
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
731
@param hostApi Pointer to host API struct
733
@param hostApiIndex Index of current (HPI) host API
735
@return PortAudio error code
737
PaError PaAsiHpi_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex )
739
PaError result = paNoError;
740
PaAsiHpiHostApiRepresentation *hpiHostApi = NULL;
741
PaHostApiInfo *baseApiInfo;
743
/* Allocate host API structure */
744
PA_UNLESS_( hpiHostApi = (PaAsiHpiHostApiRepresentation*) PaUtil_AllocateMemory(
745
sizeof(PaAsiHpiHostApiRepresentation) ), paInsufficientMemory );
746
PA_UNLESS_( hpiHostApi->allocations = PaUtil_CreateAllocationGroup(), paInsufficientMemory );
748
hpiHostApi->hostApiIndex = hostApiIndex;
749
hpiHostApi->subSys = NULL;
751
/* Try to initialize HPI subsystem */
752
if( ( hpiHostApi->subSys = HPI_SubSysCreate() ) == NULL)
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" ));
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) ));
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;
780
PA_ENSURE_( PaAsiHpi_BuildDeviceList( hpiHostApi ) );
782
(*hostApi)->Terminate = Terminate;
783
(*hostApi)->OpenStream = OpenStream;
784
(*hostApi)->IsFormatSupported = IsFormatSupported;
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 );
792
PaUtil_InitializeStreamInterface( &hpiHostApi->blockingStreamInterface, CloseStream, StartStream,
793
StopStream, AbortStream, IsStreamStopped, IsStreamActive,
794
GetStreamTime, PaUtil_DummyGetCpuLoad,
795
ReadStream, WriteStream, GetStreamReadAvailable, GetStreamWriteAvailable );
797
/* Store identity of main thread */
798
PA_ENSURE_( PaUnixThreading_Initialize() );
802
/* Clean up memory */
803
Terminate( (PaUtilHostApiRepresentation *)hpiHostApi );
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.
812
@param Pointer to host API struct
814
static void Terminate( struct PaUtilHostApiRepresentation *hostApi )
816
PaAsiHpiHostApiRepresentation *hpiHostApi = (PaAsiHpiHostApiRepresentation*)hostApi;
818
PaError result = paNoError;
822
/* Get rid of HPI-specific structures */
823
if( hpiHostApi->subSys )
825
HW16 lastAdapterIndex = HPI_MAX_ADAPTERS;
826
/* Iterate through device list and close adapters */
827
for( i=0; i < hostApi->info.deviceCount; ++i )
829
PaAsiHpiDeviceInfo *hpiDevice = (PaAsiHpiDeviceInfo *) hostApi->deviceInfos[ i ];
830
/* Close adapter only if it differs from previous one */
831
if( hpiDevice->adapterIndex != lastAdapterIndex )
833
/* Ignore errors (report only during debugging) */
834
PA_ASIHPI_UNLESS_( HPI_AdapterClose( hpiHostApi->subSys,
835
hpiDevice->adapterIndex ), paNoError );
836
lastAdapterIndex = hpiDevice->adapterIndex;
839
/* Finally dismantle HPI subsystem */
840
HPI_SubSysFree( hpiHostApi->subSys );
843
if( hpiHostApi->allocations )
845
PaUtil_FreeAllAllocations( hpiHostApi->allocations );
846
PaUtil_DestroyAllocationGroup( hpiHostApi->allocations );
849
PaUtil_FreeMemory( hpiHostApi );
856
/** Converts PortAudio sample format to equivalent HPI format.
858
@param paFormat PortAudio sample format
860
@return HPI sample format
862
static HW16 PaAsiHpi_PaToHpiFormat( PaSampleFormat paFormat )
864
/* Ignore interleaving flag */
865
switch( paFormat & ~paNonInterleaved )
868
return HPI_FORMAT_PCM32_FLOAT;
871
return HPI_FORMAT_PCM32_SIGNED;
874
return HPI_FORMAT_PCM24_SIGNED;
877
return HPI_FORMAT_PCM16_SIGNED;
880
return HPI_FORMAT_PCM8_UNSIGNED;
882
/* Default is 16-bit signed */
885
return HPI_FORMAT_PCM16_SIGNED;
890
/** Converts HPI sample format to equivalent PortAudio format.
892
@param paFormat HPI sample format
894
@return PortAudio sample format
896
static PaSampleFormat PaAsiHpi_HpiToPaFormat( HW16 hpiFormat )
900
case HPI_FORMAT_PCM32_FLOAT:
903
case HPI_FORMAT_PCM32_SIGNED:
906
case HPI_FORMAT_PCM24_SIGNED:
909
case HPI_FORMAT_PCM16_SIGNED:
912
case HPI_FORMAT_PCM8_UNSIGNED:
915
/* Default is custom format (e.g. for HPI MP3 format) */
917
return paCustomFormat;
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
927
@param hostApi Pointer to host API struct
929
@param parameters Pointer to stream parameter struct
931
@param sampleRate Desired sample rate
933
@param hpiDevice Pointer to HPI device struct
935
@param hpiFormat Resulting HPI format returned here
937
@return PortAudio error code (typically indicating a problem with stream format)
939
static PaError PaAsiHpi_CreateFormat( struct PaUtilHostApiRepresentation *hostApi,
940
const PaStreamParameters *parameters, double sampleRate,
941
PaAsiHpiDeviceInfo **hpiDevice, HPI_FORMAT *hpiFormat )
943
int maxChannelCount = 0;
944
PaSampleFormat hostSampleFormat = 0;
947
/* Unless alternate device specification is supported, reject the use of
948
paUseHostApiSpecificDeviceSpecification */
949
if( parameters->device == paUseHostApiSpecificDeviceSpecification )
950
return paInvalidDevice;
953
assert( parameters->device < hostApi->info.deviceCount );
954
*hpiDevice = (PaAsiHpiDeviceInfo*) hostApi->deviceInfos[ parameters->device ];
957
/* Validate streamInfo - this implementation doesn't use custom stream info */
958
if( parameters->hostApiSpecificStreamInfo )
959
return paIncompatibleHostApiSpecificStreamInfo;
961
/* Check that device can support channel count */
962
if( (*hpiDevice)->streamIsOutput )
964
maxChannelCount = (*hpiDevice)->baseDeviceInfo.maxOutputChannels;
968
maxChannelCount = (*hpiDevice)->baseDeviceInfo.maxInputChannels;
970
if( (maxChannelCount == 0) || (parameters->channelCount > maxChannelCount) )
971
return paInvalidChannelCount;
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;
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 );
987
PA_ASIHPI_REPORT_ERROR_( hpiError );
990
case HPI_ERROR_INVALID_FORMAT:
991
return paSampleFormatNotSupported;
993
case HPI_ERROR_INVALID_SAMPLERATE:
994
case HPI_ERROR_INCOMPATIBLE_SAMPLERATE:
995
return paInvalidSampleRate;
997
case HPI_ERROR_INVALID_CHANNELS:
998
return paInvalidChannelCount;
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.
1010
@param hostApi Pointer to host API struct
1012
@param hpiDevice Pointer to HPI device struct
1014
@param hpiFormat Pointer to HPI format struct
1016
@return PortAudio error code (typically indicating a problem with stream format or device)
1018
static PaError PaAsiHpi_OpenInput( struct PaUtilHostApiRepresentation *hostApi,
1019
const PaAsiHpiDeviceInfo *hpiDevice, const HPI_FORMAT *hpiFormat,
1020
HPI_HISTREAM *hpiStream )
1022
PaAsiHpiHostApiRepresentation *hpiHostApi = (PaAsiHpiHostApiRepresentation*)hostApi;
1023
PaError result = paNoError;
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 );
1036
PA_ASIHPI_REPORT_ERROR_( hpiError );
1037
PA_ASIHPI_UNLESS_( HPI_InStreamClose( hpiHostApi->subSys, *hpiStream ), paNoError );
1040
case HPI_ERROR_INVALID_FORMAT:
1041
return paSampleFormatNotSupported;
1043
case HPI_ERROR_INVALID_SAMPLERATE:
1044
case HPI_ERROR_INCOMPATIBLE_SAMPLERATE:
1045
return paInvalidSampleRate;
1047
case HPI_ERROR_INVALID_CHANNELS:
1048
return paInvalidChannelCount;
1051
/* In case anything else went wrong */
1052
return paInvalidDevice;
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.
1065
@param hostApi Pointer to host API struct
1067
@param hpiDevice Pointer to HPI device struct
1069
@param hpiFormat Pointer to HPI format struct
1071
@return PortAudio error code (typically indicating a problem with stream format or device)
1073
static PaError PaAsiHpi_OpenOutput( struct PaUtilHostApiRepresentation *hostApi,
1074
const PaAsiHpiDeviceInfo *hpiDevice, const HPI_FORMAT *hpiFormat,
1075
HPI_HOSTREAM *hpiStream )
1077
PaAsiHpiHostApiRepresentation *hpiHostApi = (PaAsiHpiHostApiRepresentation*)hostApi;
1078
PaError result = paNoError;
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 );
1087
/* Check output format (format is set on first write to output stream) */
1088
hpiError = HPI_OutStreamQueryFormat( hpiHostApi->subSys, *hpiStream, (HPI_FORMAT*)hpiFormat );
1091
PA_ASIHPI_REPORT_ERROR_( hpiError );
1092
PA_ASIHPI_UNLESS_( HPI_OutStreamClose( hpiHostApi->subSys, *hpiStream ), paNoError );
1095
case HPI_ERROR_INVALID_FORMAT:
1096
return paSampleFormatNotSupported;
1098
case HPI_ERROR_INVALID_SAMPLERATE:
1099
case HPI_ERROR_INCOMPATIBLE_SAMPLERATE:
1100
return paInvalidSampleRate;
1102
case HPI_ERROR_INVALID_CHANNELS:
1103
return paInvalidChannelCount;
1106
/* In case anything else went wrong */
1107
return paInvalidDevice;
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.
1120
@param hostApi Pointer to host API struct
1122
@param inputParameters Pointer to stream parameter struct for input side of stream
1124
@param outputParameters Pointer to stream parameter struct for output side of stream
1126
@param sampleRate Desired sample rate
1128
@return PortAudio error code (paFormatIsSupported on success)
1130
static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
1131
const PaStreamParameters *inputParameters,
1132
const PaStreamParameters *outputParameters,
1135
PaError result = paFormatIsSupported;
1136
PaAsiHpiHostApiRepresentation *hpiHostApi = (PaAsiHpiHostApiRepresentation*)hostApi;
1137
PaAsiHpiDeviceInfo *hpiDevice = NULL;
1138
HPI_FORMAT hpiFormat;
1141
if( inputParameters )
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 );
1157
if( outputParameters )
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 );
1176
/* ---------------------------- Stream Interface ---------------------------- */
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.
1182
@param streamComp Pointer to stream component (input or output) to query
1184
@param info Pointer to stream info struct that will contain result
1186
@return PortAudio error code (either paNoError, paDeviceUnavailable or paUnanticipatedHostError)
1188
static PaError PaAsiHpi_GetStreamInfo( PaAsiHpiStreamComponent *streamComp, PaAsiHpiStreamInfo *info )
1190
PaError result = paDeviceUnavailable;
1192
HW32 bufferSize, dataSize, frameCounter, auxDataSize, threshold;
1193
HW32 hwBufferSize, hwDataSize;
1195
assert( streamComp );
1198
/* First blank the stream info struct, in case something goes wrong below.
1199
This saves the caller from initializing the struct. */
1201
info->bufferSize = 0;
1203
info->frameCounter = 0;
1204
info->auxDataSize = 0;
1205
info->totalBufferedData = 0;
1206
info->availableFrames = 0;
1207
info->underflow = 0;
1210
if( streamComp->hpiDevice && streamComp->hpiStream )
1212
/* Obtain detailed stream info (either input or output) */
1213
if( streamComp->hpiDevice->streamIsOutput )
1215
PA_ASIHPI_UNLESS_( HPI_OutStreamGetInfoEx( streamComp->hpiDevice->subSys,
1216
streamComp->hpiStream,
1217
&state, &bufferSize, &dataSize, &frameCounter,
1218
&auxDataSize ), paUnanticipatedHostError );
1222
PA_ASIHPI_UNLESS_( HPI_InStreamGetInfoEx( streamComp->hpiDevice->subSys,
1223
streamComp->hpiStream,
1224
&state, &bufferSize, &dataSize, &frameCounter,
1225
&auxDataSize ), paUnanticipatedHostError );
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);
1265
/** Display stream component information for debugging purposes.
1267
@param streamComp Pointer to stream component (input or output) to query
1269
@param stream Pointer to stream struct which contains the component above
1271
static void PaAsiHpi_StreamComponentDump( PaAsiHpiStreamComponent *streamComp,
1272
PaAsiHpiStream *stream )
1274
PaAsiHpiStreamInfo streamInfo;
1276
assert( streamComp );
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 )
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 )
1290
PA_DEBUG(( "interleaved channels, " ));
1294
PA_DEBUG(( "non-interleaved channels, " ));
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 ));
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 )
1313
PA_DEBUG(( "interleaved channels, " ));
1317
PA_DEBUG(( "non-interleaved channels, " ));
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 ));
1328
switch( stream->bufferProcessor.hostBufferSizeMode )
1330
case paUtilFixedHostBufferSize:
1331
PA_DEBUG(( "[fixed] " ));
1333
case paUtilBoundedHostBufferSize:
1334
PA_DEBUG(( "[bounded] " ));
1336
case paUtilUnknownHostBufferSize:
1337
PA_DEBUG(( "[unknown] " ));
1339
case paUtilVariableHostBufferSizePartialUsageAllowed:
1340
PA_DEBUG(( "[variable] " ));
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 )
1355
case HPI_STATE_STOPPED:
1356
PA_DEBUG(( "[STOPPED] " ));
1358
case HPI_STATE_PLAYING:
1359
PA_DEBUG(( "[PLAYING] " ));
1361
case HPI_STATE_RECORDING:
1362
PA_DEBUG(( "[RECORDING] " ));
1364
case HPI_STATE_DRAINED:
1365
PA_DEBUG(( "[DRAINED] " ));
1368
PA_DEBUG(( "[unknown state] " ));
1371
if( streamComp->hostBufferSize )
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 ));
1379
PA_DEBUG(( "hw = %d/%d B, ", streamInfo.dataSize, streamComp->hardwareBufferSize ));
1381
PA_DEBUG(( "count = %d", streamInfo.frameCounter ));
1382
if( streamInfo.overflow )
1384
PA_DEBUG(( " [overflow]" ));
1386
else if( streamInfo.underflow )
1388
PA_DEBUG(( " [underflow]" ));
1394
/** Display stream information for debugging purposes.
1396
@param stream Pointer to stream to query
1398
static void PaAsiHpi_StreamDump( PaAsiHpiStream *stream )
1402
PA_DEBUG(( "\n------------------------- STREAM INFO FOR %p ---------------------------\n", stream ));
1403
/* General stream info (input+output) */
1404
if( stream->baseStreamRep.streamCallback )
1406
PA_DEBUG(( "[callback] " ));
1410
PA_DEBUG(( "[blocking] " ));
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 )
1417
case paAsiHpiStoppedState:
1418
PA_DEBUG(( "[stopped]\n" ));
1420
case paAsiHpiActiveState:
1421
PA_DEBUG(( "[active]\n" ));
1423
case paAsiHpiCallbackFinishedState:
1424
PA_DEBUG(( "[cb fin]\n" ));
1427
PA_DEBUG(( "[unknown state]\n" ));
1430
if( stream->callbackMode )
1432
PA_DEBUG(( "cb info: thread=%p, cbAbort=%d, cbFinished=%d\n",
1433
stream->thread.thread, stream->callbackAbort, stream->callbackFinished ));
1436
PA_DEBUG(( "----------------------------------- Input ------------------------------------\n" ));
1439
PaAsiHpi_StreamComponentDump( stream->input, stream );
1443
PA_DEBUG(( "*none*\n" ));
1446
PA_DEBUG(( "----------------------------------- Output ------------------------------------\n" ));
1447
if( stream->output )
1449
PaAsiHpi_StreamComponentDump( stream->output, stream );
1453
PA_DEBUG(( "*none*\n" ));
1455
PA_DEBUG(( "-------------------------------------------------------------------------------\n\n" ));
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.
1472
@param streamComp Pointer to stream component struct
1474
@param pollingInterval Polling interval for stream, in milliseconds
1476
@param framesPerPaHostBuffer Size of PortAudio host buffer, in frames
1478
@param suggestedLatency Suggested latency for stream component, in seconds
1480
@return PortAudio error code (possibly paBufferTooBig or paInsufficientMemory)
1482
static PaError PaAsiHpi_SetupBuffers( PaAsiHpiStreamComponent *streamComp, HW32 pollingInterval,
1483
unsigned long framesPerPaHostBuffer, PaTime suggestedLatency )
1485
PaError result = paNoError;
1486
PaAsiHpiStreamInfo streamInfo;
1487
unsigned long hpiBufferSize = 0, paHostBufferSize = 0;
1489
assert( streamComp );
1490
assert( streamComp->hpiDevice );
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;
1499
/* Check if BBM (background bus mastering) is to be enabled */
1500
if( PA_ASIHPI_USE_BBM_ )
1502
HW32 bbmBufferSize = 0, preLatencyBufferSize = 0;
1504
PaTime pollingOverhead;
1506
/* Check overhead of Pa_Sleep() call (minimum sleep duration in ms -> OS dependent) */
1507
pollingOverhead = PaUtil_GetTime();
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) */
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 )
1524
PaTime bufferDuration = ((PaTime)bbmBufferSize) / streamComp->bytesPerFrame
1525
/ streamComp->hpiFormat.dwSampleRate;
1526
/* Don't decrease buffer */
1527
if( bufferDuration < suggestedLatency )
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 );
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,
1546
hpiError = HPI_InStreamHostBufferAllocate( streamComp->hpiDevice->subSys,
1547
streamComp->hpiStream,
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 )
1557
/* Retry BBM allocation with smaller size if requested latency proved too big */
1558
if( preLatencyBufferSize > 0 )
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,
1568
hpiError = HPI_InStreamHostBufferAllocate( streamComp->hpiDevice->subSys,
1569
streamComp->hpiStream,
1571
/* Another round of error checking */
1574
PA_ASIHPI_REPORT_ERROR_( hpiError );
1575
/* No escapes this time */
1576
if( hpiError == HPI_ERROR_INVALID_DATASIZE )
1578
result = paBufferTooBig;
1581
else if( hpiError != HPI_ERROR_INVALID_OPERATION )
1583
result = paUnanticipatedHostError;
1589
streamComp->hostBufferSize = bbmBufferSize;
1590
hpiBufferSize = bbmBufferSize;
1595
result = paBufferTooBig;
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 )
1603
result = paUnanticipatedHostError;
1609
hpiBufferSize = bbmBufferSize;
1613
/* Final check of buffer size */
1614
paHostBufferSize = streamComp->bytesPerFrame * framesPerPaHostBuffer;
1615
if( hpiBufferSize < 3*paHostBufferSize )
1617
result = paBufferTooBig;
1620
/* Set cap on output buffer size, based on latency suggestions */
1621
if( streamComp->hpiDevice->streamIsOutput )
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;
1633
streamComp->outputBufferCap = 0;
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 );
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.
1651
See pa_hostapi.h for a list of validity guarantees made about OpenStream parameters.
1653
@param hostApi Pointer to host API struct
1655
@param s List of open streams, where successfully opened stream will go
1657
@param inputParameters Pointer to stream parameter struct for input side of stream
1659
@param outputParameters Pointer to stream parameter struct for output side of stream
1661
@param sampleRate Desired sample rate
1663
@param framesPerBuffer Desired number of frames per buffer passed to user callback
1664
(or chunk size for blocking stream)
1666
@param streamFlags Stream flags
1668
@param streamCallback Pointer to user callback function (zero for blocking interface)
1670
@param userData Pointer to user data that will be passed to callback function along with data
1672
@return PortAudio error code
1674
static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
1676
const PaStreamParameters *inputParameters,
1677
const PaStreamParameters *outputParameters,
1679
unsigned long framesPerBuffer,
1680
PaStreamFlags streamFlags,
1681
PaStreamCallback *streamCallback,
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;
1693
/* Validate platform-specific flags -> none expected for HPI */
1694
if( (streamFlags & paPlatformSpecificFlags) != 0 )
1695
return paInvalidFlag; /* unexpected platform-specific flag */
1697
/* Create blank stream structure */
1698
PA_UNLESS_( stream = (PaAsiHpiStream *)PaUtil_AllocateMemory( sizeof(PaAsiHpiStream) ),
1699
paInsufficientMemory );
1700
memset( stream, 0, sizeof(PaAsiHpiStream) );
1702
/* If the number of frames per buffer is unspecified, we have to come up with one. */
1703
if( framesPerHostBuffer == paFramesPerBufferUnspecified )
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 );
1713
/* AudioScience cards like BIG buffers by default */
1714
framesPerHostBuffer = 4096;
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 );
1731
/* Open underlying streams, check formats and allocate buffers */
1732
if( inputParameters )
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 ) );
1753
if( outputParameters )
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 ) );
1775
/* Determine maximum frames per host buffer (least common denominator of input/output) */
1776
if( inputParameters && outputParameters )
1778
stream->maxFramesPerHostBuffer = PA_MIN( stream->input->tempBufferSize / stream->input->bytesPerFrame,
1779
stream->output->tempBufferSize / stream->output->bytesPerFrame );
1783
stream->maxFramesPerHostBuffer = inputParameters ? stream->input->tempBufferSize / stream->input->bytesPerFrame
1784
: stream->output->tempBufferSize / stream->output->bytesPerFrame;
1786
assert( stream->maxFramesPerHostBuffer > 0 );
1787
/* Initialize various other stream parameters */
1788
stream->neverDropInput = streamFlags & paNeverDropInput;
1789
stream->state = paAsiHpiStoppedState;
1791
/* Initialize either callback or blocking interface */
1792
if( streamCallback )
1794
PaUtil_InitializeStreamRepresentation( &stream->baseStreamRep,
1795
&hpiHostApi->callbackStreamInterface,
1796
streamCallback, userData );
1797
stream->callbackMode = 1;
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;
1810
PaUtil_InitializeCpuLoadMeasurer( &stream->cpuLoadMeasurer, sampleRate );
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 ) );
1821
stream->baseStreamRep.streamInfo.structVersion = 1;
1822
stream->baseStreamRep.streamInfo.sampleRate = sampleRate;
1823
/* Determine input latency from buffer processor and buffer sizes */
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 );
1833
/* Determine output latency from buffer processor and buffer sizes */
1834
if( stream->output )
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) )
1841
bufferDuration = PA_MIN( bufferDuration,
1842
stream->output->outputBufferCap / sampleRate / stream->output->bytesPerFrame );
1844
stream->baseStreamRep.streamInfo.outputLatency =
1845
PaUtil_GetBufferProcessorOutputLatency( &stream->bufferProcessor ) +
1846
bufferDuration - stream->maxFramesPerHostBuffer / sampleRate;
1847
assert( stream->baseStreamRep.streamInfo.outputLatency > 0.0 );
1850
/* Report stream info, for debugging purposes */
1851
PaAsiHpi_StreamDump( stream );
1853
/* Save initialized stream to PA stream list */
1854
*s = (PaStream*)stream;
1858
CloseStream( (PaStream*)stream );
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.
1868
@param s Pointer to PortAudio stream
1870
@return PortAudio error code
1872
static PaError CloseStream( PaStream *s )
1874
PaError result = paNoError;
1875
PaAsiHpiStream *stream = (PaAsiHpiStream*)s;
1877
/* If stream is already gone, all is well */
1878
if( stream == NULL )
1881
/* Generic stream cleanup */
1882
PaUtil_TerminateBufferProcessor( &stream->bufferProcessor );
1883
PaUtil_TerminateStreamRepresentation( &stream->baseStreamRep );
1885
/* Implementation-specific details - close internal streams */
1888
/* Close HPI stream (freeing BBM host buffer in the process, if used) */
1889
if( stream->input->hpiStream )
1891
PA_ASIHPI_UNLESS_( HPI_InStreamClose( stream->input->hpiDevice->subSys,
1892
stream->input->hpiStream ), paUnanticipatedHostError );
1894
/* Free temp buffer and stream component */
1895
PaUtil_FreeMemory( stream->input->tempBuffer );
1896
PaUtil_FreeMemory( stream->input );
1898
if( stream->output )
1900
/* Close HPI stream (freeing BBM host buffer in the process, if used) */
1901
if( stream->output->hpiStream )
1903
PA_ASIHPI_UNLESS_( HPI_OutStreamClose( stream->output->hpiDevice->subSys,
1904
stream->output->hpiStream ), paUnanticipatedHostError );
1906
/* Free temp buffer and stream component */
1907
PaUtil_FreeMemory( stream->output->tempBuffer );
1908
PaUtil_FreeMemory( stream->output );
1911
PaUtil_FreeMemory( stream->blockingUserBufferCopy );
1912
PaUtil_FreeMemory( stream );
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.
1924
@param stream Pointer to stream struct
1926
@return PortAudio error code
1928
static PaError PaAsiHpi_PrimeOutputWithSilence( PaAsiHpiStream *stream )
1930
PaError result = paNoError;
1931
PaAsiHpiStreamComponent *out;
1932
PaUtilZeroer *zeroer;
1933
PaSampleFormat outputFormat;
1934
#if (HPI_VER < HPI_VERSION_CONSTRUCTOR( 3, 5, 5 ))
1938
out = stream->output;
1939
/* Only continue if stream has output channels */
1942
assert( out->tempBuffer );
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 );
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 );
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.
1976
@todo Implement priming via the user callback
1978
@param stream Pointer to stream struct
1980
@param outputPrimed True if output is already primed (if false, silence will be loaded before starting)
1982
@return PortAudio error code
1984
static PaError PaAsiHpi_StartStream( PaAsiHpiStream *stream, int outputPrimed )
1986
PaError result = paNoError;
1990
PA_ASIHPI_UNLESS_( HPI_InStreamStart( stream->input->hpiDevice->subSys,
1991
stream->input->hpiStream ), paUnanticipatedHostError );
1993
if( stream->output )
1997
/* Buffer isn't primed, so load stream with silence */
1998
PA_ENSURE_( PaAsiHpi_PrimeOutputWithSilence( stream ) );
2000
PA_ASIHPI_UNLESS_( HPI_OutStreamStart( stream->output->hpiDevice->subSys,
2001
stream->output->hpiStream ), paUnanticipatedHostError );
2003
stream->state = paAsiHpiActiveState;
2004
stream->callbackFinished = 0;
2006
/* Report stream info for debugging purposes */
2007
/* PaAsiHpi_StreamDump( stream ); */
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
2020
@param s Pointer to PortAudio stream
2022
@return PortAudio error code
2024
static PaError StartStream( PaStream *s )
2026
PaError result = paNoError;
2027
PaAsiHpiStream *stream = (PaAsiHpiStream*)s;
2031
/* Ready the processor */
2032
PaUtil_ResetBufferProcessor( &stream->bufferProcessor );
2034
if( stream->callbackMode )
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*/ ) );
2042
PA_ENSURE_( PaAsiHpi_StartStream( stream, 0 ) );
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.
2054
This function is safe to call from the callback engine thread as well as the main thread.
2056
@param stream Pointer to stream struct
2058
@param abort True if samples in output buffer should be discarded (otherwise blocks until stream is done)
2060
@return PortAudio error code
2063
static PaError PaAsiHpi_StopStream( PaAsiHpiStream *stream, int abort )
2065
PaError result = paNoError;
2069
/* Input channels */
2072
PA_ASIHPI_UNLESS_( HPI_InStreamReset( stream->input->hpiDevice->subSys,
2073
stream->input->hpiStream ), paUnanticipatedHostError );
2075
/* Output channels */
2076
if( stream->output )
2080
/* Wait until HPI output stream is drained */
2083
PaAsiHpiStreamInfo streamInfo;
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_) )
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 ) );
2098
PA_ASIHPI_UNLESS_( HPI_OutStreamReset( stream->output->hpiDevice->subSys,
2099
stream->output->hpiStream ), paUnanticipatedHostError );
2102
/* Report stream info for debugging purposes */
2103
/* PaAsiHpi_StreamDump( stream ); */
2110
/** Stop or abort PortAudio stream.
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.
2119
Don't call this from the callback engine thread!
2121
@param stream Pointer to stream struct
2123
@param abort True if samples in output buffer should be discarded (otherwise blocks until stream is done)
2125
@return PortAudio error code
2127
static PaError PaAsiHpi_ExplicitStop( PaAsiHpiStream *stream, int abort )
2129
PaError result = paNoError;
2131
/* First deal with the callback thread, cancelling and/or joining it if necessary */
2132
if( stream->callbackMode )
2135
stream->callbackAbort = abort;
2138
PA_DEBUG(( "Aborting callback\n" ));
2142
PA_DEBUG(( "Stopping callback\n" ));
2144
PA_ENSURE_( PaUnixThread_Terminate( &stream->thread, !abort, &threadRes ) );
2145
if( threadRes != paNoError )
2147
PA_DEBUG(( "Callback thread returned: %d\n", threadRes ));
2152
PA_ENSURE_( PaAsiHpi_StopStream( stream, abort ) );
2155
stream->state = paAsiHpiStoppedState;
2162
/** Stop PortAudio stream.
2163
This blocks until the output buffers are drained.
2165
@param s Pointer to PortAudio stream
2167
@return PortAudio error code
2169
static PaError StopStream( PaStream *s )
2171
return PaAsiHpi_ExplicitStop( (PaAsiHpiStream *) s, 0 );
2175
/** Abort PortAudio stream.
2176
This discards any existing data in output buffers and stops the stream immediately.
2178
@param s Pointer to PortAudio stream
2180
@return PortAudio error code
2182
static PaError AbortStream( PaStream *s )
2184
return PaAsiHpi_ExplicitStop( (PaAsiHpiStream * ) s, 1 );
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).
2193
@param s Pointer to PortAudio stream
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.
2199
static PaError IsStreamStopped( PaStream *s )
2201
PaAsiHpiStream *stream = (PaAsiHpiStream*)s;
2204
return stream->state == paAsiHpiStoppedState ? 1 : 0;
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.
2214
@param s Pointer to PortAudio stream
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.
2220
static PaError IsStreamActive( PaStream *s )
2222
PaAsiHpiStream *stream = (PaAsiHpiStream*)s;
2225
return stream->state == paAsiHpiActiveState ? 1 : 0;
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
2234
@param s Pointer to PortAudio stream
2236
@return Stream time, in seconds
2238
static PaTime GetStreamTime( PaStream *s )
2240
return PaUtil_GetTime();
2244
/** Returns CPU load.
2246
@param s Pointer to PortAudio stream
2248
@return CPU load (0.0 if blocking interface is used)
2250
static double GetStreamCpuLoad( PaStream *s )
2252
PaAsiHpiStream *stream = (PaAsiHpiStream*)s;
2254
return stream->callbackMode ? PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer ) : 0.0;
2257
/* --------------------------- Callback Interface --------------------------- */
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.
2265
@param userData A pointer to an open stream previously created with Pa_OpenStream
2267
static void PaAsiHpi_OnThreadExit( void *userData )
2269
PaAsiHpiStream *stream = (PaAsiHpiStream *) userData;
2273
PaUtil_ResetCpuLoadMeasurer( &stream->cpuLoadMeasurer );
2275
PA_DEBUG(( "%s: Stopping HPI streams\n", __FUNCTION__ ));
2276
PaAsiHpi_StopStream( stream, stream->callbackAbort );
2277
PA_DEBUG(( "%s: Stoppage\n", __FUNCTION__ ));
2279
/* Eventually notify user all buffers have played */
2280
if( stream->baseStreamRep.streamFinishedCallback )
2282
stream->baseStreamRep.streamFinishedCallback( stream->baseStreamRep.userData );
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;
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.
2302
@param stream Pointer to stream struct
2304
@param framesAvail Returns the number of available frames
2306
@param cbFlags Overflows and underflows indicated in here
2308
@return PortAudio error code (only paUnanticipatedHostError expected)
2310
static PaError PaAsiHpi_WaitForFrames( PaAsiHpiStream *stream, unsigned long *framesAvail,
2311
PaStreamCallbackFlags *cbFlags )
2313
PaError result = paNoError;
2315
unsigned long framesTarget;
2316
HW32 outputData = 0, outputSpace = 0, inputData = 0, framesLeft = 0;
2319
assert( stream->input || stream->output );
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 );
2328
PaAsiHpiStreamInfo info;
2329
/* Check output first, as this takes priority in the default full-duplex mode */
2330
if( stream->output )
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 )
2336
framesLeft = framesTarget - info.availableFrames;
2337
Pa_Sleep( (long)ceil( 1000 * framesLeft / sampleRate ) );
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 ) )
2349
framesLeft = info.totalBufferedData - stream->output->outputBufferCap / stream->output->bytesPerFrame;
2350
Pa_Sleep( (long)ceil( 1000 * framesLeft / sampleRate ) );
2353
outputData = info.totalBufferedData;
2354
outputSpace = info.availableFrames;
2355
/* Report output underflow to callback */
2356
if( info.underflow )
2358
*cbFlags |= paOutputUnderflow;
2362
/* Now check input side */
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 )
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) )
2374
Pa_Sleep( (long)ceil( 1000 * framesLeft / sampleRate ) );
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... */
2389
*cbFlags |= paInputOverflow;
2394
/* Full-duplex stream */
2395
if( stream->input && stream->output )
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;
2405
*framesAvail = stream->input ? inputData : outputSpace;
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.
2422
If the current timestamp is the present, the input timestamp is in the past and the output
2423
timestamp is in the future.
2425
@param stream Pointer to stream struct
2427
@param timeInfo Pointer to timeInfo struct that will contain timestamps
2429
static void PaAsiHpi_CalculateTimeInfo( PaAsiHpiStream *stream, PaStreamCallbackTimeInfo *timeInfo )
2431
PaAsiHpiStreamInfo streamInfo;
2436
sampleRate = stream->baseStreamRep.streamInfo.sampleRate;
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;
2445
PaAsiHpi_GetStreamInfo( stream->input, &streamInfo );
2446
timeInfo->inputBufferAdcTime -= streamInfo.totalBufferedData / sampleRate;
2448
/* The first of the outgoing samples will be played after all the samples in the output
2450
timeInfo->outputBufferDacTime = timeInfo->currentTime;
2451
if( stream->output )
2453
PaAsiHpi_GetStreamInfo( stream->output, &streamInfo );
2454
timeInfo->outputBufferDacTime += streamInfo.totalBufferedData / sampleRate;
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.
2464
@param stream Pointer to stream struct
2466
@param numFrames On entrance the number of available frames, on exit the number of
2469
@param cbFlags Indicates overflows and underflows
2471
@return PortAudio error code
2473
static PaError PaAsiHpi_BeginProcessing( PaAsiHpiStream *stream, unsigned long *numFrames,
2474
PaStreamCallbackFlags *cbFlags )
2476
PaError result = paNoError;
2479
if( *numFrames > stream->maxFramesPerHostBuffer )
2480
*numFrames = stream->maxFramesPerHostBuffer;
2484
PaAsiHpiStreamInfo info;
2486
#if (HPI_VER < HPI_VERSION_CONSTRUCTOR( 3, 5, 5 ))
2489
HW32 framesToGet = *numFrames;
2491
/* Check for overflows and underflows yet again */
2492
PA_ENSURE_( PaAsiHpi_GetStreamInfo( stream->input, &info ) );
2495
*cbFlags |= paInputOverflow;
2497
/* Input underflow if less than expected number of samples pitch up */
2498
if( framesToGet > info.availableFrames )
2500
PaUtilZeroer *zeroer;
2501
PaSampleFormat inputFormat;
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) );
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 );
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 );
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 );
2537
if( stream->output )
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 );
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.
2557
@param stream Pointer to stream struct
2559
@param numFrames The number of frames to write to the output stream
2561
@param cbFlags Indicates overflows and underflows
2563
static PaError PaAsiHpi_EndProcessing( PaAsiHpiStream *stream, unsigned long numFrames,
2564
PaStreamCallbackFlags *cbFlags )
2566
PaError result = paNoError;
2570
if( stream->output )
2572
PaAsiHpiStreamInfo info;
2573
#if (HPI_VER < HPI_VERSION_CONSTRUCTOR( 3, 5, 5 ))
2576
/* Check for underflows after the (potentially time-consuming) callback */
2577
PA_ENSURE_( PaAsiHpi_GetStreamInfo( stream->output, &info ) );
2578
if( info.underflow )
2580
*cbFlags |= paOutputUnderflow;
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 );
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 );
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.
2614
@param userData A pointer to an open stream previously created with Pa_OpenStream.
2616
static void *CallbackThreadFunc( void *userData )
2618
PaError result = paNoError;
2619
PaAsiHpiStream *stream = (PaAsiHpiStream *) userData;
2620
int callbackResult = paContinue;
2624
/* Cleanup routine stops streams on thread exit */
2625
pthread_cleanup_push( &PaAsiHpi_OnThreadExit, stream );
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 ) );
2636
PaStreamCallbackFlags cbFlags = 0;
2637
unsigned long framesAvail, framesGot;
2639
pthread_testcancel();
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) )
2646
PA_DEBUG(( "Setting callbackResult to paComplete\n" ));
2647
callbackResult = paComplete;
2650
/* Start winding down thread if requested */
2651
if( callbackResult != paContinue )
2653
stream->callbackAbort = (callbackResult == paAbort);
2654
if( stream->callbackAbort ||
2655
/** @concern BlockAdaption: Go on if adaption buffers are empty */
2656
PaUtil_IsBufferProcessorOutputEmpty( &stream->bufferProcessor ) )
2660
PA_DEBUG(( "%s: Flushing buffer processor\n", __FUNCTION__ ));
2661
/* There is still buffered output that needs to be processed */
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 ) );
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 )
2674
PaStreamCallbackTimeInfo timeInfo = {0, 0, 0};
2676
pthread_testcancel();
2678
framesGot = framesAvail;
2679
if( stream->bufferProcessor.hostBufferSizeMode == paUtilFixedHostBufferSize )
2681
/* We've committed to a fixed host buffer size, stick to that */
2682
framesGot = framesGot >= stream->maxFramesPerHostBuffer ? stream->maxFramesPerHostBuffer : 0;
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 );
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 );
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) )
2703
/* Special full-duplex paNeverDropInput mode */
2704
if( stream->neverDropInput )
2706
PaUtil_SetNoOutput( &stream->bufferProcessor );
2707
cbFlags |= paOutputOverflow;
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) */
2715
/* WRITE TO HPI OUTPUT STREAM */
2716
PA_ENSURE_( PaAsiHpi_EndProcessing( stream, framesGot, &cbFlags ) );
2717
/* Advance frame counter */
2718
framesAvail -= framesGot;
2720
PaUtil_EndCpuLoadMeasurement( &stream->cpuLoadMeasurer, framesGot );
2722
if( framesGot == 0 )
2724
/* Go back to polling for more frames */
2728
if( callbackResult != paContinue )
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 );
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 );
2750
/* --------------------------- Blocking Interface --------------------------- */
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. */
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.
2759
@param s Pointer to PortAudio stream
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)
2764
@param frames Number of frames to read from stream
2766
@return PortAudio error code (also indicates overflow via paInputOverflowed)
2768
static PaError ReadStream( PaStream *s,
2770
unsigned long frames )
2772
PaError result = paNoError;
2773
PaAsiHpiStream *stream = (PaAsiHpiStream*)s;
2774
PaAsiHpiStreamInfo info;
2778
PA_UNLESS_( stream->input, paCanNotReadFromAnOutputOnlyStream );
2780
/* Check for input overflow since previous call to ReadStream */
2781
PA_ENSURE_( PaAsiHpi_GetStreamInfo( stream->input, &info ) );
2784
result = paInputOverflowed;
2787
/* NB Make copy of user buffer pointers, since they are advanced by buffer processor */
2788
if( stream->bufferProcessor.userInputIsInterleaved )
2790
userBuffer = buffer;
2794
/* Copy channels into local array */
2795
userBuffer = stream->blockingUserBufferCopy;
2796
memcpy( userBuffer, buffer, sizeof (void *) * stream->input->hpiFormat.wChannels );
2801
unsigned long framesGot, framesAvail;
2802
PaStreamCallbackFlags cbFlags = 0;
2804
PA_ENSURE_( PaAsiHpi_WaitForFrames( stream, &framesAvail, &cbFlags ) );
2805
framesGot = PA_MIN( framesAvail, frames );
2806
PA_ENSURE_( PaAsiHpi_BeginProcessing( stream, &framesGot, &cbFlags ) );
2810
framesGot = PaUtil_CopyInput( &stream->bufferProcessor, &userBuffer, framesGot );
2811
PA_ENSURE_( PaAsiHpi_EndProcessing( stream, framesGot, &cbFlags ) );
2812
/* Advance frame counter */
2813
frames -= framesGot;
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.
2826
@param s Pointer to PortAudio stream
2828
@param buffer Pointer to buffer that provides interleaved data (or an array of pointers
2829
to a buffer for each non-interleaved channel)
2831
@param frames Number of frames to write to stream
2833
@return PortAudio error code (also indicates underflow via paOutputUnderflowed)
2835
static PaError WriteStream( PaStream *s,
2837
unsigned long frames )
2839
PaError result = paNoError;
2840
PaAsiHpiStream *stream = (PaAsiHpiStream*)s;
2841
PaAsiHpiStreamInfo info;
2842
const void *userBuffer;
2845
PA_UNLESS_( stream->output, paCanNotWriteToAnInputOnlyStream );
2847
/* Check for output underflow since previous call to WriteStream */
2848
PA_ENSURE_( PaAsiHpi_GetStreamInfo( stream->output, &info ) );
2849
if( info.underflow )
2851
result = paOutputUnderflowed;
2854
/* NB Make copy of user buffer pointers, since they are advanced by buffer processor */
2855
if( stream->bufferProcessor.userOutputIsInterleaved )
2857
userBuffer = buffer;
2861
/* Copy channels into local array */
2862
userBuffer = stream->blockingUserBufferCopy;
2863
memcpy( (void *)userBuffer, buffer, sizeof (void *) * stream->output->hpiFormat.wChannels );
2868
unsigned long framesGot, framesAvail;
2869
PaStreamCallbackFlags cbFlags = 0;
2871
PA_ENSURE_( PaAsiHpi_WaitForFrames( stream, &framesAvail, &cbFlags ) );
2872
framesGot = PA_MIN( framesAvail, frames );
2873
PA_ENSURE_( PaAsiHpi_BeginProcessing( stream, &framesGot, &cbFlags ) );
2877
framesGot = PaUtil_CopyOutput( &stream->bufferProcessor, &userBuffer, framesGot );
2878
PA_ENSURE_( PaAsiHpi_EndProcessing( stream, framesGot, &cbFlags ) );
2879
/* Advance frame counter */
2880
frames -= framesGot;
2889
/** Number of frames that can be read from input stream without blocking.
2891
@param s Pointer to PortAudio stream
2893
@return Number of frames, or PortAudio error code
2895
static signed long GetStreamReadAvailable( PaStream *s )
2897
PaError result = paNoError;
2898
PaAsiHpiStream *stream = (PaAsiHpiStream*)s;
2899
PaAsiHpiStreamInfo info;
2902
PA_UNLESS_( stream->input, paCanNotReadFromAnOutputOnlyStream );
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;
2909
result = paInputOverflowed;
2917
/** Number of frames that can be written to output stream without blocking.
2919
@param s Pointer to PortAudio stream
2921
@return Number of frames, or PortAudio error code
2923
static signed long GetStreamWriteAvailable( PaStream *s )
2925
PaError result = paNoError;
2926
PaAsiHpiStream *stream = (PaAsiHpiStream*)s;
2927
PaAsiHpiStreamInfo info;
2930
PA_UNLESS_( stream->output, paCanNotWriteToAnInputOnlyStream );
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 )
2937
result = paOutputUnderflowed;