/[pcsx2_0.9.7]/trunk/3rdparty/portaudio/src/hostapi/asihpi/pa_linux_asihpi.c
ViewVC logotype

Contents of /trunk/3rdparty/portaudio/src/hostapi/asihpi/pa_linux_asihpi.c

Parent Directory Parent Directory | Revision Log Revision Log


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

  ViewVC Help
Powered by ViewVC 1.1.22