[Julius-cvs 593] CVS update: julius4/libsent/src/adin/pa/hostapi/asihpi

アーカイブの一覧に戻る

sumom****@users***** sumom****@users*****
2011年 2月 5日 (土) 18:00:02 JST


Index: julius4/libsent/src/adin/pa/hostapi/asihpi/pa_linux_asihpi.c
diff -u /dev/null julius4/libsent/src/adin/pa/hostapi/asihpi/pa_linux_asihpi.c:1.1
--- /dev/null	Sat Feb  5 18:00:02 2011
+++ julius4/libsent/src/adin/pa/hostapi/asihpi/pa_linux_asihpi.c	Sat Feb  5 18:00:02 2011
@@ -0,0 +1,2910 @@
+/*
+ * PortAudio Portable Real-Time Audio Library
+ * Latest Version at: http://www.portaudio.com
+ *
+ * PortAudio v18 version of AudioScience HPI driver by Fred Gleason <fredg****@salem*****>
+ * PortAudio v19 version of AudioScience HPI driver by Ludwig Schwardt <schwa****@sun*****>
+ *
+ * Copyright (c) 2003 Fred Gleason
+ * Copyright (c) 2005,2006 Ludwig Schwardt
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/*
+ * The text above constitutes the entire PortAudio license; however,
+ * the PortAudio community also makes the following non-binding requests:
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version. It is also
+ * requested that these non-binding requests be included along with the
+ * license above.
+ */
+
+/*
+ * Modification History
+ * 12/2003 - Initial version
+ * 09/2005 - v19 version [rewrite]
+ */
+
+/** @file
+ @ingroup hostapi_src
+ @brief Host API implementation supporting AudioScience cards
+        via the Linux HPI interface.
+
+ <h3>Overview</h3>
+
+ This is a PortAudio implementation for the AudioScience HPI Audio API
+ on the Linux platform. AudioScience makes a range of audio adapters customised
+ for the broadcasting industry, with support for both Windows and Linux.
+ More information on their products can be found on their website:
+
+     http://www.audioscience.com
+
+ Documentation for the HPI API can be found at:
+
+     http://www.audioscience.com/internet/download/sdk/spchpi.pdf
+
+ The Linux HPI driver itself (a kernel module + library) can be downloaded from:
+
+     http://www.audioscience.com/internet/download/linux_drivers.htm
+
+ <h3>Implementation strategy</h3>
+
+ *Note* Ideally, AudioScience cards should be handled by the PortAudio ALSA
+ implementation on Linux, as ALSA is the preferred Linux soundcard API. The existence
+ of this host API implementation might therefore seem a bit flawed. Unfortunately, at
+ the time of the creation of this implementation (June 2006), the PA ALSA implementation
+ could not make use of the existing AudioScience ALSA driver. PA ALSA uses the
+ "memory-mapped" (mmap) ALSA access mode to interact with the ALSA library, while the
+ AudioScience ALSA driver only supports the "read-write" access mode. The appropriate
+ solution to this problem is to add "read-write" support to PortAudio ALSA, thereby
+ extending the range of soundcards it supports (AudioScience cards are not the only
+ ones with this problem). Given the author's limited knowledge of ALSA and the
+ simplicity of the HPI API, the second-best solution was born...
+
+ The following mapping between HPI and PA was followed:
+ HPI subsystem => PortAudio host API
+ HPI adapter => nothing specific
+ HPI stream => PortAudio device
+
+ Each HPI stream is either input or output (not both), and can support
+ different channel counts, sampling rates and sample formats. It is therefore
+ a more natural fit to a PA device. A PA stream can therefore combine two
+ HPI streams (one input and one output) into a "full-duplex" stream. These
+ HPI streams can even be on different physical adapters. The two streams ought to be
+ sample-synchronised when they reside on the same adapter, as most AudioScience adapters
+ derive their ADC and DAC clocks from one master clock. When combining two adapters
+ into one full-duplex stream, however, the use of a word clock connection between the
+ adapters is strongly recommended.
+
+ The HPI interface is inherently blocking, making use of read and write calls to
+ transfer data between user buffers and driver buffers. The callback interface therefore
+ requires a helper thread ("callback engine") which periodically transfers data (one thread
+ per PA stream, in fact). The current implementation explicitly sleeps via Pa_Sleep() until
+ enough samples can be transferred (select() or poll() would be better, but currently seems
+ impossible...). The thread implementation makes use of the Unix thread helper functions
+ and some pthread calls here and there. If a unified PA thread exists, this host API
+ implementation might also compile on Windows, as this is the only real Linux-specific
+ part of the code.
+
+ There is no inherent fixed buffer size in the HPI interface, as in some other host APIs.
+ The PortAudio implementation contains a buffer that is allocated during OpenStream and
+ used to transfer data between the callback and the HPI driver buffer. The size of this
+ buffer is quite flexible and is derived from latency suggestions and matched to the
+ requested callback buffer size as far as possible. It can become quite huge, as the
+ AudioScience cards are typically geared towards higher-latency applications and contain
+ large hardware buffers.
+
+ The HPI interface natively supports most common sample formats and sample rates (some
+ conversion is done on the adapter itself).
+
+ Stream time is measured based on the number of processed frames, which is adjusted by the
+ number of frames currently buffered by the HPI driver.
+
+ There is basic support for detecting overflow and underflow. The HPI interface does not
+ explicitly indicate this, so thresholds on buffer levels are used in combination with
+ stream state. Recovery from overflow and underflow is left to the PA client.
+
+ Blocking streams are also implemented. It makes use of the same polling routines that
+ the callback interface uses, in order to prevent the allocation of variable-sized
+ buffers during reading and writing. The framesPerBuffer parameter is therefore still
+ relevant, and this can be increased in the blocking case to improve efficiency.
+
+ The implementation contains extensive reporting macros (slightly modified PA_ENSURE and
+ PA_UNLESS versions) and a useful stream dump routine to provide debugging feedback.
+
+ Output buffer priming via the user callback (i.e. paPrimeOutputBuffersUsingStreamCallback
+ and friends) is not implemented yet. All output is primed with silence.
+
+ Please send bug reports etc. to Ludwig Schwardt <schwa****@sun*****>
+ */
+
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>          /* strlen() */
+#include <pthread.h>         /* pthreads and friends */
+#include <assert.h>          /* assert */
+#include <math.h>            /* ceil, floor */
+
+#include <asihpi/hpi.h>      /* HPI API */
+
+#include "portaudio.h"       /* PortAudio API */
+#include "pa_util.h"         /* PA_DEBUG, other small utilities */
+#include "pa_unix_util.h"    /* Unix threading utilities */
+#include "pa_allocation.h"   /* Group memory allocation */
+#include "pa_hostapi.h"      /* Host API structs */
+#include "pa_stream.h"       /* Stream interface structs */
+#include "pa_cpuload.h"      /* CPU load measurer */
+#include "pa_process.h"      /* Buffer processor */
+#include "pa_converters.h"   /* PaUtilZeroer */
+#include "pa_debugprint.h"
+
+/* -------------------------------------------------------------------------- */
+
+/*
+ * Defines
+ */
+
+/* Error reporting and assertions */
+
+/** Evaluate expression, and return on any PortAudio errors */
+#define PA_ENSURE_(expr) \
+    do { \
+        PaError paError = (expr); \
+        if( UNLIKELY( paError < paNoError ) ) \
+        { \
+            PA_DEBUG(( "Expression '" #expr "' failed in '" __FILE__ "', line: " STRINGIZE( __LINE__ ) "\n" )); \
+            result = paError; \
+            goto error; \
+        } \
+    } while (0);
+
+/** Assert expression, else return the provided PaError */
+#define PA_UNLESS_(expr, paError) \
+    do { \
+        if( UNLIKELY( (expr) == 0 ) ) \
+        { \
+            PA_DEBUG(( "Expression '" #expr "' failed in '" __FILE__ "', line: " STRINGIZE( __LINE__ ) "\n" )); \
+            result = (paError); \
+            goto error; \
+        } \
+    } while( 0 );
+
+/** Check return value of HPI function, and map it to PaError */
+#define PA_ASIHPI_UNLESS_(expr, paError) \
+    do { \
+        HW16 hpiError = (expr); \
+        /* If HPI error occurred */ \
+        if( UNLIKELY( hpiError ) ) \
+        { \
+	    char szError[256]; \
+	    HPI_GetErrorText( hpiError, szError ); \
+	    PA_DEBUG(( "HPI error %d occurred: %s\n", hpiError, szError )); \
+	    /* This message will always be displayed, even if debug info is disabled */ \
+            PA_DEBUG(( "Expression '" #expr "' failed in '" __FILE__ "', line: " STRINGIZE( __LINE__ ) "\n" )); \
+            if( (paError) == paUnanticipatedHostError ) \
+	    { \
+	        PA_DEBUG(( "Host error description: %s\n", szError )); \
+	        /* PaUtil_SetLastHostErrorInfo should only be used in the main thread */ \
+	        if( pthread_equal( pthread_self(), paUnixMainThread ) ) \
+                { \
+		    PaUtil_SetLastHostErrorInfo( paInDevelopment, hpiError, szError ); \
+                } \
+	    } \
+	    /* If paNoError is specified, continue as usual */ \
+            /* (useful if you only want to print out the debug messages above) */ \
+	    if( (paError) < 0 ) \
+	    { \
+	        result = (paError); \
+	        goto error; \
+	    } \
+        } \
+    } while( 0 );
+
+/** Report HPI error code and text */
+#define PA_ASIHPI_REPORT_ERROR_(hpiErrorCode) \
+    do { \
+        char szError[256]; \
+        HPI_GetErrorText( hpiError, szError ); \
+        PA_DEBUG(( "HPI error %d occurred: %s\n", hpiError, szError )); \
+        /* PaUtil_SetLastHostErrorInfo should only be used in the main thread */ \
+        if( pthread_equal( pthread_self(), paUnixMainThread ) ) \
+	{ \
+	    PaUtil_SetLastHostErrorInfo( paInDevelopment, (hpiErrorCode), szError ); \
+	} \
+    } while( 0 );
+
+/* Defaults */
+
+/** Sample formats available natively on AudioScience hardware */
+#define PA_ASIHPI_AVAILABLE_FORMATS_ (paFloat32 | paInt32 | paInt24 | paInt16 | paUInt8)
+/** Enable background bus mastering (BBM) for buffer transfers, if available (see HPI docs) */
+#define PA_ASIHPI_USE_BBM_ 1
+/** Minimum number of frames in HPI buffer (for either data or available space).
+ If buffer contains less data/space, it indicates xrun or completion. */
+#define PA_ASIHPI_MIN_FRAMES_ 1152
+/** Minimum polling interval in milliseconds, which determines minimum host buffer size */
+#define PA_ASIHPI_MIN_POLLING_INTERVAL_ 10
+
+/* -------------------------------------------------------------------------- */
+
+/*
+ * Structures
+ */
+
+/** Host API global data */
+typedef struct PaAsiHpiHostApiRepresentation
+{
+    /* PortAudio "base class" - keep the baseRep first! (C-style inheritance) */
+    PaUtilHostApiRepresentation baseHostApiRep;
+    PaUtilStreamInterface callbackStreamInterface;
+    PaUtilStreamInterface blockingStreamInterface;
+
+    PaUtilAllocationGroup *allocations;
+
+    /* implementation specific data goes here */
+
+    PaHostApiIndex hostApiIndex;
+    /** HPI subsystem pointer */
+    HPI_HSUBSYS *subSys;
+}
+PaAsiHpiHostApiRepresentation;
+
+
+/** Device data */
+typedef struct PaAsiHpiDeviceInfo
+{
+    /* PortAudio "base class" - keep the baseRep first! (C-style inheritance) */
+    /** Common PortAudio device information */
+    PaDeviceInfo baseDeviceInfo;
+
+    /* implementation specific data goes here */
+
+    /** HPI subsystem (required for most HPI calls) */
+    HPI_HSUBSYS *subSys;
+    /** Adapter index */
+    HW16 adapterIndex;
+    /** Adapter model number (hex) */
+    HW16 adapterType;
+    /** Adapter HW/SW version */
+    HW16 adapterVersion;
+    /** Adapter serial number */
+    HW32 adapterSerialNumber;
+    /** Stream number */
+    HW16 streamIndex;
+    /** 0=Input, 1=Output (HPI streams are either input or output but not both) */
+    HW16 streamIsOutput;
+}
+PaAsiHpiDeviceInfo;
+
+
+/** Stream state as defined by PortAudio.
+ It seems that the host API implementation has to keep track of the PortAudio stream state.
+ Please note that this is NOT the same as the state of the underlying HPI stream. By separating
+ these two concepts, a lot of flexibility is gained. There is a rough match between the two,
+ of course, but forcing a precise match is difficult. For example, HPI_STATE_DRAINED can occur
+ during the Active state of PortAudio (due to underruns) and also during CallBackFinished in
+ the case of an output stream. Similarly, HPI_STATE_STOPPED mostly coincides with the Stopped
+ PortAudio state, by may also occur in the CallbackFinished state when recording is finished.
+
+ Here is a rough match-up:
+
+ PortAudio state   =>     HPI state
+ ---------------          ---------
+ Active            =>     HPI_STATE_RECORDING, HPI_STATE_PLAYING, (HPI_STATE_DRAINED)
+ Stopped           =>     HPI_STATE_STOPPED
+ CallbackFinished  =>     HPI_STATE_STOPPED, HPI_STATE_DRAINED */
+typedef enum PaAsiHpiStreamState
+{
+    paAsiHpiStoppedState=0,
+    paAsiHpiActiveState=1,
+    paAsiHpiCallbackFinishedState=2
+}
+PaAsiHpiStreamState;
+
+
+/** Stream component data (associated with one direction, i.e. either input or output) */
+typedef struct PaAsiHpiStreamComponent
+{
+    /** Device information (HPI handles, etc) */
+    PaAsiHpiDeviceInfo *hpiDevice;
+    /** Stream handle, as passed to HPI interface.
+     HACK: we assume types HPI_HISTREAM and HPI_HOSTREAM are the same...
+     (both are HW32 up to version 3.00 of ASIHPI, and hopefully they stay that way) */
+    HPI_HISTREAM hpiStream;
+    /** Stream format, as passed to HPI interface */
+    HPI_FORMAT hpiFormat;
+    /** Number of bytes per frame, derived from hpiFormat and saved for convenience */
+    HW32 bytesPerFrame;
+    /** Size of hardware (on-card) buffer of stream in bytes */
+    HW32 hardwareBufferSize;
+    /** Size of host (BBM) buffer of stream in bytes (if used) */
+    HW32 hostBufferSize;
+    /** Upper limit on the utilization of output stream buffer (both hardware and host).
+     This prevents large latencies in an output-only stream with a potentially huge buffer
+     and a fast data generator, which would otherwise keep the hardware buffer filled to
+     capacity. See also the "Hardware Buffering=off" option in the AudioScience WAV driver. */
+    HW32 outputBufferCap;
+    /** Sample buffer (halfway station between HPI and buffer processor) */
+    HW8 *tempBuffer;
+    /** Sample buffer size, in bytes */
+    HW32 tempBufferSize;
+}
+PaAsiHpiStreamComponent;
+
+
+/** Stream data */
+typedef struct PaAsiHpiStream
+{
+    /* PortAudio "base class" - keep the baseRep first! (C-style inheritance) */
+    PaUtilStreamRepresentation baseStreamRep;
+    PaUtilCpuLoadMeasurer cpuLoadMeasurer;
+    PaUtilBufferProcessor bufferProcessor;
+
+    PaUtilAllocationGroup *allocations;
+
+    /* implementation specific data goes here */
+
+    /** Separate structs for input and output sides of stream */
+    PaAsiHpiStreamComponent *input, *output;
+
+    /** Polling interval (in milliseconds) */
+    HW32 pollingInterval;
+    /** Are we running in callback mode? */
+    int callbackMode;
+    /** Number of frames to transfer at a time to/from HPI */
+    unsigned long maxFramesPerHostBuffer;
+    /** Indicates that the stream is in the paNeverDropInput mode */
+    int neverDropInput;
+    /** Contains copy of user buffers, used by blocking interface to transfer non-interleaved data.
+     It went here instead of to each stream component, as the stream component buffer setup in
+     PaAsiHpi_SetupBuffers doesn't know the stream details such as callbackMode.
+     (Maybe a problem later if ReadStream and WriteStream happens concurrently on same stream.) */
+    void **blockingUserBufferCopy;
+
+    /* Thread-related variables */
+
+    /** Helper thread which will deliver data to user callback */
+    PaUnixThread thread;
+    /** PortAudio stream state (Active/Stopped/CallbackFinished) */
+    volatile sig_atomic_t state;
+    /** Hard abort, i.e. drop frames? */
+    volatile sig_atomic_t callbackAbort;
+    /** True if stream stopped via exiting callback with paComplete/paAbort flag
+     (as opposed to explicit call to StopStream/AbortStream) */
+    volatile sig_atomic_t callbackFinished;
+}
+PaAsiHpiStream;
+
+
+/** Stream state information, collected together for convenience */
+typedef struct PaAsiHpiStreamInfo
+{
+    /** HPI stream state (HPI_STATE_STOPPED, HPI_STATE_PLAYING, etc.) */
+    HW16 state;
+    /** Size (in bytes) of recording/playback data buffer in HPI driver */
+    HW32 bufferSize;
+    /** Amount of data (in bytes) available in the buffer */
+    HW32 dataSize;
+    /** Number of frames played/recorded since last stream reset */
+    HW32 frameCounter;
+    /** Amount of data (in bytes) in hardware (on-card) buffer.
+     This differs from dataSize if bus mastering (BBM) is used, which introduces another
+     driver-level buffer to which dataSize/bufferSize then refers. */
+    HW32 auxDataSize;
+    /** Total number of data frames currently buffered by HPI driver (host + hw buffers) */
+    HW32 totalBufferedData;
+    /** Size of immediately available data (for input) or space (for output) in frames.
+     This only checks the first-level buffer (typically host buffer). This amount can be
+     transferred immediately. */
+    HW32 availableFrames;
+    /** Indicates that hardware buffer is getting too full */
+    int overflow;
+    /** Indicates that hardware buffer is getting too empty */
+    int underflow;
+}
+PaAsiHpiStreamInfo;
+
+/* -------------------------------------------------------------------------- */
+
+/*
+ * Function prototypes
+ */
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+    /* The only exposed function in the entire host API implementation */
+    PaError PaAsiHpi_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+static void Terminate( struct PaUtilHostApiRepresentation *hostApi );
+static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
+                                  const PaStreamParameters *inputParameters,
+                                  const PaStreamParameters *outputParameters,
+                                  double sampleRate );
+
+/* Stream prototypes */
+static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
+                           PaStream **s,
+                           const PaStreamParameters *inputParameters,
+                           const PaStreamParameters *outputParameters,
+                           double sampleRate,
+                           unsigned long framesPerBuffer,
+                           PaStreamFlags streamFlags,
+                           PaStreamCallback *streamCallback,
+                           void *userData );
+static PaError CloseStream( PaStream *s );
+static PaError StartStream( PaStream *s );
+static PaError StopStream( PaStream *s );
+static PaError AbortStream( PaStream *s );
+static PaError IsStreamStopped( PaStream *s );
+static PaError IsStreamActive( PaStream *s );
+static PaTime GetStreamTime( PaStream *s );
+static double GetStreamCpuLoad( PaStream *s );
+
+/* Blocking prototypes */
+static PaError ReadStream( PaStream *s, void *buffer, unsigned long frames );
+static PaError WriteStream( PaStream *s, const void *buffer, unsigned long frames );
+static signed long GetStreamReadAvailable( PaStream *s );
+static signed long GetStreamWriteAvailable( PaStream *s );
+
+/* Callback prototypes */
+static void *CallbackThreadFunc( void *userData );
+
+/* Functions specific to this API */
+static PaError PaAsiHpi_BuildDeviceList( PaAsiHpiHostApiRepresentation *hpiHostApi );
+static HW16 PaAsiHpi_PaToHpiFormat( PaSampleFormat paFormat );
+static PaSampleFormat PaAsiHpi_HpiToPaFormat( HW16 hpiFormat );
+static PaError PaAsiHpi_CreateFormat( struct PaUtilHostApiRepresentation *hostApi,
+                                      const PaStreamParameters *parameters, double sampleRate,
+                                      PaAsiHpiDeviceInfo **hpiDevice, HPI_FORMAT *hpiFormat );
+static PaError PaAsiHpi_OpenInput( struct PaUtilHostApiRepresentation *hostApi,
+                                   const PaAsiHpiDeviceInfo *hpiDevice, const HPI_FORMAT *hpiFormat,
+                                   HPI_HISTREAM *hpiStream );
+static PaError PaAsiHpi_OpenOutput( struct PaUtilHostApiRepresentation *hostApi,
+                                    const PaAsiHpiDeviceInfo *hpiDevice, const HPI_FORMAT *hpiFormat,
+                                    HPI_HOSTREAM *hpiStream );
+static PaError PaAsiHpi_GetStreamInfo( PaAsiHpiStreamComponent *streamComp, PaAsiHpiStreamInfo *info );
+static void PaAsiHpi_StreamComponentDump( PaAsiHpiStreamComponent *streamComp, PaAsiHpiStream *stream );
+static void PaAsiHpi_StreamDump( PaAsiHpiStream *stream );
+static PaError PaAsiHpi_SetupBuffers( PaAsiHpiStreamComponent *streamComp, HW32 pollingInterval,
+                                      unsigned long framesPerPaHostBuffer, PaTime suggestedLatency );
+static PaError PaAsiHpi_PrimeOutputWithSilence( PaAsiHpiStream *stream );
+static PaError PaAsiHpi_StartStream( PaAsiHpiStream *stream, int outputPrimed );
+static PaError PaAsiHpi_StopStream( PaAsiHpiStream *stream, int abort );
+static PaError PaAsiHpi_ExplicitStop( PaAsiHpiStream *stream, int abort );
+static void PaAsiHpi_OnThreadExit( void *userData );
+static PaError PaAsiHpi_WaitForFrames( PaAsiHpiStream *stream, unsigned long *framesAvail,
+                                       PaStreamCallbackFlags *cbFlags );
+static void PaAsiHpi_CalculateTimeInfo( PaAsiHpiStream *stream, PaStreamCallbackTimeInfo *timeInfo );
+static PaError PaAsiHpi_BeginProcessing( PaAsiHpiStream* stream, unsigned long* numFrames,
+        PaStreamCallbackFlags *cbFlags );
+static PaError PaAsiHpi_EndProcessing( PaAsiHpiStream *stream, unsigned long numFrames,
+                                       PaStreamCallbackFlags *cbFlags );
+
+/* ==========================================================================
+ * ============================= IMPLEMENTATION =============================
+ * ========================================================================== */
+
+/* --------------------------- Host API Interface --------------------------- */
+
+/** Enumerate all PA devices (= HPI streams).
+ This compiles a list of all HPI adapters, and registers a PA device for each input and
+ output stream it finds. Most errors are ignored, as missing or erroneous devices are
+ simply skipped.
+
+ @param hpiHostApi Pointer to HPI host API struct
+
+ @return PortAudio error code (only paInsufficientMemory in practice)
+ */
+static PaError PaAsiHpi_BuildDeviceList( PaAsiHpiHostApiRepresentation *hpiHostApi )
+{
+    PaError result = paNoError;
+    PaUtilHostApiRepresentation *hostApi = &hpiHostApi->baseHostApiRep;
+    PaHostApiInfo *baseApiInfo = &hostApi->info;
+    PaAsiHpiDeviceInfo *hpiDeviceList;
+    HW16 adapterList[ HPI_MAX_ADAPTERS ];
+    HW16 numAdapters;
+    HW16 hpiError = 0;
+    int i, j, deviceCount = 0, deviceIndex = 0;
+
+    assert( hpiHostApi );
+    assert( hpiHostApi->subSys );
+
+    /* Look for adapters (not strictly necessary, as AdapterOpen can do the same, but this */
+    /* way we have less errors since we do not try to open adapters we know aren't there) */
+    /* Errors not considered critical here (subsystem may report 0 devices), but report them */
+    /* in debug mode. */
+    PA_ASIHPI_UNLESS_( HPI_SubSysFindAdapters( hpiHostApi->subSys, &numAdapters,
+                       adapterList, HPI_MAX_ADAPTERS ), paNoError );
+
+    /* First open and count the number of devices (= number of streams), to ease memory allocation */
+    for( i=0; i < HPI_MAX_ADAPTERS; ++i )
+    {
+        HW16 inStreams, outStreams;
+        HW16 version;
+        HW32 serial;
+        HW16 type;
+
+        /* If no adapter found at this index, skip it */
+        if( adapterList[i] == 0 )
+            continue;
+
+        /* Try to open adapter */
+        hpiError = HPI_AdapterOpen( hpiHostApi->subSys, i );
+        /* Report error and skip to next device on failure */
+        if( hpiError )
+        {
+            PA_ASIHPI_REPORT_ERROR_( hpiError );
+            continue;
+        }
+        hpiError = HPI_AdapterGetInfo( hpiHostApi->subSys, i,
+                                       &outStreams, &inStreams, &version, &serial, &type );
+        /* Skip to next device on failure */
+        if( hpiError )
+        {
+            PA_ASIHPI_REPORT_ERROR_( hpiError );
+            continue;
+        }
+        else
+        {
+            /* Assign default devices if available and increment device count */
+            if( (baseApiInfo->defaultInputDevice == paNoDevice) && (inStreams > 0) )
+                baseApiInfo->defaultInputDevice = deviceCount;
+            deviceCount += inStreams;
+            if( (baseApiInfo->defaultOutputDevice == paNoDevice) && (outStreams > 0) )
+                baseApiInfo->defaultOutputDevice = deviceCount;
+            deviceCount += outStreams;
+        }
+    }
+
+    /* Register any discovered devices */
+    if( deviceCount > 0 )
+    {
+        /* Memory allocation */
+        PA_UNLESS_( hostApi->deviceInfos = (PaDeviceInfo**) PaUtil_GroupAllocateMemory(
+                                               hpiHostApi->allocations, sizeof(PaDeviceInfo*) * deviceCount ),
+                    paInsufficientMemory );
+        /* Allocate all device info structs in a contiguous block */
+        PA_UNLESS_( hpiDeviceList = (PaAsiHpiDeviceInfo*) PaUtil_GroupAllocateMemory(
+                                        hpiHostApi->allocations, sizeof(PaAsiHpiDeviceInfo) * deviceCount ),
+                    paInsufficientMemory );
+
+        /* Now query devices again for information */
+        for( i=0; i < HPI_MAX_ADAPTERS; ++i )
+        {
+            HW16 inStreams, outStreams;
+            HW16 version;
+            HW32 serial;
+            HW16 type;
+
+            /* If no adapter found at this index, skip it */
+            if( adapterList[i] == 0 )
+                continue;
+
+            /* Assume adapter is still open from previous round */
+            hpiError = HPI_AdapterGetInfo( hpiHostApi->subSys, i,
+                                           &outStreams, &inStreams, &version, &serial, &type );
+            /* Report error and skip to next device on failure */
+            if( hpiError )
+            {
+                PA_ASIHPI_REPORT_ERROR_( hpiError );
+                continue;
+            }
+            else
+            {
+                PA_DEBUG(( "Found HPI Adapter ID=%4X Idx=%d #In=%d #Out=%d S/N=%d HWver=%c%d DSPver=%03d\n",
+                           type, i, inStreams, outStreams, serial,
+                           ((version>>3)&0xf)+'A',                  /* Hw version major */
+                           version&0x7,                             /* Hw version minor */
+                           ((version>>13)*100)+((version>>7)&0x3f)  /* DSP code version */
+                         ));
+            }
+
+            /* First add all input streams as devices */
+            for( j=0; j < inStreams; ++j )
+            {
+                PaAsiHpiDeviceInfo *hpiDevice = &hpiDeviceList[deviceIndex];
+                PaDeviceInfo *baseDeviceInfo = &hpiDevice->baseDeviceInfo;
+                char srcName[72];
+                char *deviceName;
+
+                memset( hpiDevice, 0, sizeof(PaAsiHpiDeviceInfo) );
+                /* Set implementation-specific device details */
+                hpiDevice->subSys = hpiHostApi->subSys;
+                hpiDevice->adapterIndex = i;
+                hpiDevice->adapterType = type;
+                hpiDevice->adapterVersion = version;
+                hpiDevice->adapterSerialNumber = serial;
+                hpiDevice->streamIndex = j;
+                hpiDevice->streamIsOutput = 0;
+                /* Set common PortAudio device stats */
+                baseDeviceInfo->structVersion = 2;
+                /* Make sure name string is owned by API info structure */
+                sprintf( srcName,
+                         "Adapter %d (%4X) - Input Stream %d", i+1, type, j+1 );
+                PA_UNLESS_( deviceName = (char *) PaUtil_GroupAllocateMemory(
+                                             hpiHostApi->allocations, strlen(srcName) + 1 ), paInsufficientMemory );
+                strcpy( deviceName, srcName );
+                baseDeviceInfo->name = deviceName;
+                baseDeviceInfo->hostApi = hpiHostApi->hostApiIndex;
+                baseDeviceInfo->maxInputChannels = HPI_MAX_CHANNELS;
+                baseDeviceInfo->maxOutputChannels = 0;
+                /* Default latency values for interactive performance */
+                baseDeviceInfo->defaultLowInputLatency = 0.01;
+                baseDeviceInfo->defaultLowOutputLatency = -1.0;
+                /* Default latency values for robust non-interactive applications (eg. playing sound files) */
+                baseDeviceInfo->defaultHighInputLatency = 0.2;
+                baseDeviceInfo->defaultHighOutputLatency = -1.0;
+                /* HPI interface can actually handle any sampling rate to 1 Hz accuracy,
+                * so this default is as good as any */
+                baseDeviceInfo->defaultSampleRate = 44100;
+
+                /* Store device in global PortAudio list */
+                hostApi->deviceInfos[deviceIndex++] = (PaDeviceInfo *) hpiDevice;
+            }
+
+            /* Now add all output streams as devices (I know, the repetition is painful) */
+            for( j=0; j < outStreams; ++j )
+            {
+                PaAsiHpiDeviceInfo *hpiDevice = &hpiDeviceList[deviceIndex];
+                PaDeviceInfo *baseDeviceInfo = &hpiDevice->baseDeviceInfo;
+                char srcName[72];
+                char *deviceName;
+
+                memset( hpiDevice, 0, sizeof(PaAsiHpiDeviceInfo) );
+                /* Set implementation-specific device details */
+                hpiDevice->subSys = hpiHostApi->subSys;
+                hpiDevice->adapterIndex = i;
+                hpiDevice->adapterType = type;
+                hpiDevice->adapterVersion = version;
+                hpiDevice->adapterSerialNumber = serial;
+                hpiDevice->streamIndex = j;
+                hpiDevice->streamIsOutput = 1;
+                /* Set common PortAudio device stats */
+                baseDeviceInfo->structVersion = 2;
+                /* Make sure name string is owned by API info structure */
+                sprintf( srcName,
+                         "Adapter %d (%4X) - Output Stream %d", i+1, type, j+1 );
+                PA_UNLESS_( deviceName = (char *) PaUtil_GroupAllocateMemory(
+                                             hpiHostApi->allocations, strlen(srcName) + 1 ), paInsufficientMemory );
+                strcpy( deviceName, srcName );
+                baseDeviceInfo->name = deviceName;
+                baseDeviceInfo->hostApi = hpiHostApi->hostApiIndex;
+                baseDeviceInfo->maxInputChannels = 0;
+                baseDeviceInfo->maxOutputChannels = HPI_MAX_CHANNELS;
+                /* Default latency values for interactive performance. */
+                baseDeviceInfo->defaultLowInputLatency = -1.0;
+                baseDeviceInfo->defaultLowOutputLatency = 0.01;
+                /* Default latency values for robust non-interactive applications (eg. playing sound files). */
+                baseDeviceInfo->defaultHighInputLatency = -1.0;
+                baseDeviceInfo->defaultHighOutputLatency = 0.2;
+                /* HPI interface can actually handle any sampling rate to 1 Hz accuracy,
+                * so this default is as good as any */
+                baseDeviceInfo->defaultSampleRate = 44100;
+
+                /* Store device in global PortAudio list */
+                hostApi->deviceInfos[deviceIndex++] = (PaDeviceInfo *) hpiDevice;
+            }
+        }
+    }
+
+    /* Finally acknowledge checked devices */
+    baseApiInfo->deviceCount = deviceIndex;
+
+error:
+    return result;
+}
+
+
+/** Initialize host API implementation.
+ This is the only function exported beyond this file. It is called by PortAudio to initialize
+ the host API. It stores API info, finds and registers all devices, and sets up callback and
+ blocking interfaces.
+
+ @param hostApi Pointer to host API struct
+
+ @param hostApiIndex Index of current (HPI) host API
+
+ @return PortAudio error code
+ */
+PaError PaAsiHpi_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex )
+{
+    PaError result = paNoError;
+    PaAsiHpiHostApiRepresentation *hpiHostApi = NULL;
+    PaHostApiInfo *baseApiInfo;
+
+    /* Allocate host API structure */
+    PA_UNLESS_( hpiHostApi = (PaAsiHpiHostApiRepresentation*) PaUtil_AllocateMemory(
+                                 sizeof(PaAsiHpiHostApiRepresentation) ), paInsufficientMemory );
+    PA_UNLESS_( hpiHostApi->allocations = PaUtil_CreateAllocationGroup(), paInsufficientMemory );
+
+    hpiHostApi->hostApiIndex = hostApiIndex;
+    hpiHostApi->subSys = NULL;
+
+    /* Try to initialize HPI subsystem */
+    if( ( hpiHostApi->subSys = HPI_SubSysCreate() ) == NULL)
+    {
+        /* the V19 development docs say that if an implementation
+         * detects that it cannot be used, it should return a NULL
+         * interface and paNoError */
+        PA_DEBUG(( "Could not open HPI interface\n" ));
+        result = paNoError;
+        *hostApi = NULL;
+        goto error;
+    }
+    else
+    {
+        HW32 hpiVersion;
+        PA_ASIHPI_UNLESS_( HPI_SubSysGetVersion( hpiHostApi->subSys, &hpiVersion ), paUnanticipatedHostError );
+        PA_DEBUG(( "HPI interface v%d.%02d\n",
+                   hpiVersion >> 8, 10*((hpiVersion & 0xF0) >> 4) + (hpiVersion & 0x0F) ));
+    }
+
+    *hostApi = &hpiHostApi->baseHostApiRep;
+    baseApiInfo = &((*hostApi)->info);
+    /* Fill in common API details */
+    baseApiInfo->structVersion = 1;
+    baseApiInfo->type = paAudioScienceHPI;
+    baseApiInfo->name = "AudioScience HPI";
+    baseApiInfo->deviceCount = 0;
+    baseApiInfo->defaultInputDevice = paNoDevice;
+    baseApiInfo->defaultOutputDevice = paNoDevice;
+
+    PA_ENSURE_( PaAsiHpi_BuildDeviceList( hpiHostApi ) );
+
+    (*hostApi)->Terminate = Terminate;
+    (*hostApi)->OpenStream = OpenStream;
+    (*hostApi)->IsFormatSupported = IsFormatSupported;
+
+    PaUtil_InitializeStreamInterface( &hpiHostApi->callbackStreamInterface, CloseStream, StartStream,
+                                      StopStream, AbortStream, IsStreamStopped, IsStreamActive,
+                                      GetStreamTime, GetStreamCpuLoad,
+                                      PaUtil_DummyRead, PaUtil_DummyWrite,
+                                      PaUtil_DummyGetReadAvailable, PaUtil_DummyGetWriteAvailable );
+
+    PaUtil_InitializeStreamInterface( &hpiHostApi->blockingStreamInterface, CloseStream, StartStream,
+                                      StopStream, AbortStream, IsStreamStopped, IsStreamActive,
+                                      GetStreamTime, PaUtil_DummyGetCpuLoad,
+                                      ReadStream, WriteStream, GetStreamReadAvailable, GetStreamWriteAvailable );
+
+    /* Store identity of main thread */
+    PA_ENSURE_( PaUnixThreading_Initialize() );
+
+    return result;
+error:
+    /* Clean up memory */
+    Terminate( (PaUtilHostApiRepresentation *)hpiHostApi );
+    return result;
+}
+
+
+/** Terminate host API implementation.
+ This closes all HPI adapters and frees the HPI subsystem. It also frees the host API struct
+ memory. It should be called once for every PaAsiHpi_Initialize call.
+
+ @param Pointer to host API struct
+ */
+static void Terminate( struct PaUtilHostApiRepresentation *hostApi )
+{
+    PaAsiHpiHostApiRepresentation *hpiHostApi = (PaAsiHpiHostApiRepresentation*)hostApi;
+    int i;
+    PaError result = paNoError;
+
+    if( hpiHostApi )
+    {
+        /* Get rid of HPI-specific structures */
+        if( hpiHostApi->subSys )
+        {
+            HW16 lastAdapterIndex = HPI_MAX_ADAPTERS;
+            /* Iterate through device list and close adapters */
+            for( i=0; i < hostApi->info.deviceCount; ++i )
+            {
+                PaAsiHpiDeviceInfo *hpiDevice = (PaAsiHpiDeviceInfo *) hostApi->deviceInfos[ i ];
+                /* Close adapter only if it differs from previous one */
+                if( hpiDevice->adapterIndex != lastAdapterIndex )
+                {
+                    /* Ignore errors (report only during debugging) */
+                    PA_ASIHPI_UNLESS_( HPI_AdapterClose( hpiHostApi->subSys,
+                                                         hpiDevice->adapterIndex ), paNoError );
+                    lastAdapterIndex = hpiDevice->adapterIndex;
+                }
+            }
+            /* Finally dismantle HPI subsystem */
+            HPI_SubSysFree( hpiHostApi->subSys );
+        }
+
+        if( hpiHostApi->allocations )
+        {
+            PaUtil_FreeAllAllocations( hpiHostApi->allocations );
+            PaUtil_DestroyAllocationGroup( hpiHostApi->allocations );
+        }
+
+        PaUtil_FreeMemory( hpiHostApi );
+    }
+error:
+    return;
+}
+
+
+/** Converts PortAudio sample format to equivalent HPI format.
+
+ @param paFormat PortAudio sample format
+
+ @return HPI sample format
+ */
+static HW16 PaAsiHpi_PaToHpiFormat( PaSampleFormat paFormat )
+{
+    /* Ignore interleaving flag */
+    switch( paFormat & ~paNonInterleaved )
+    {
+    case paFloat32:
+        return HPI_FORMAT_PCM32_FLOAT;
+
+    case paInt32:
+        return HPI_FORMAT_PCM32_SIGNED;
+
+    case paInt24:
+        return HPI_FORMAT_PCM24_SIGNED;
+
+    case paInt16:
+        return HPI_FORMAT_PCM16_SIGNED;
+
+    case paUInt8:
+        return HPI_FORMAT_PCM8_UNSIGNED;
+
+        /* Default is 16-bit signed */
+    case paInt8:
+    default:
+        return HPI_FORMAT_PCM16_SIGNED;
+    }
+}
+
+
+/** Converts HPI sample format to equivalent PortAudio format.
+
+ @param paFormat HPI sample format
+
+ @return PortAudio sample format
+ */
+static PaSampleFormat PaAsiHpi_HpiToPaFormat( HW16 hpiFormat )
+{
+    switch( hpiFormat )
+    {
+    case HPI_FORMAT_PCM32_FLOAT:
+        return paFloat32;
+
+    case HPI_FORMAT_PCM32_SIGNED:
+        return paInt32;
+
+    case HPI_FORMAT_PCM24_SIGNED:
+        return paInt24;
+
+    case HPI_FORMAT_PCM16_SIGNED:
+        return paInt16;
+
+    case HPI_FORMAT_PCM8_UNSIGNED:
+        return paUInt8;
+
+        /* Default is custom format (e.g. for HPI MP3 format) */
+    default:
+        return paCustomFormat;
+    }
+}
+
+
+/** Creates HPI format struct based on PortAudio parameters.
+ This also does some checks to see whether the desired format is valid, and whether
+ the device allows it. This only checks the format of one half (input or output) of the
+ PortAudio stream.
+
+ @param hostApi Pointer to host API struct
+
+ @param parameters Pointer to stream parameter struct
+
+ @param sampleRate Desired sample rate
+
+ @param hpiDevice Pointer to HPI device struct
+
+ @param hpiFormat Resulting HPI format returned here
+
+ @return PortAudio error code (typically indicating a problem with stream format)
+ */
+static PaError PaAsiHpi_CreateFormat( struct PaUtilHostApiRepresentation *hostApi,
+                                      const PaStreamParameters *parameters, double sampleRate,
+                                      PaAsiHpiDeviceInfo **hpiDevice, HPI_FORMAT *hpiFormat )
+{
+    int maxChannelCount = 0;
+    PaSampleFormat hostSampleFormat = 0;
+    HW16 hpiError = 0;
+
+    /* Unless alternate device specification is supported, reject the use of
+       paUseHostApiSpecificDeviceSpecification */
+    if( parameters->device == paUseHostApiSpecificDeviceSpecification )
+        return paInvalidDevice;
+    else
+    {
+        assert( parameters->device < hostApi->info.deviceCount );
+        *hpiDevice = (PaAsiHpiDeviceInfo*) hostApi->deviceInfos[ parameters->device ];
+    }
+
+    /* Validate streamInfo - this implementation doesn't use custom stream info */
+    if( parameters->hostApiSpecificStreamInfo )
+        return paIncompatibleHostApiSpecificStreamInfo;
+
+    /* Check that device can support channel count */
+    if( (*hpiDevice)->streamIsOutput )
+    {
+        maxChannelCount = (*hpiDevice)->baseDeviceInfo.maxOutputChannels;
+    }
+    else
+    {
+        maxChannelCount = (*hpiDevice)->baseDeviceInfo.maxInputChannels;
+    }
+    if( (maxChannelCount == 0) || (parameters->channelCount > maxChannelCount) )
+        return paInvalidChannelCount;
+
+    /* All standard sample formats are supported by the buffer adapter,
+       and this implementation doesn't support any custom sample formats */
+    if( parameters->sampleFormat & paCustomFormat )
+        return paSampleFormatNotSupported;
+
+    /* Switch to closest HPI native format */
+    hostSampleFormat = PaUtil_SelectClosestAvailableFormat(PA_ASIHPI_AVAILABLE_FORMATS_,
+                       parameters->sampleFormat );
+    /* Setup format + info objects */
+    hpiError = HPI_FormatCreate( hpiFormat, (HW16)parameters->channelCount,
+                                 PaAsiHpi_PaToHpiFormat( hostSampleFormat ),
+                                 (HW32)sampleRate, 0, 0 );
+    if( hpiError )
+    {
+        PA_ASIHPI_REPORT_ERROR_( hpiError );
+        switch( hpiError )
+        {
+        case HPI_ERROR_INVALID_FORMAT:
+            return paSampleFormatNotSupported;
+
+        case HPI_ERROR_INVALID_SAMPLERATE:
+        case HPI_ERROR_INCOMPATIBLE_SAMPLERATE:
+            return paInvalidSampleRate;
+
+        case HPI_ERROR_INVALID_CHANNELS:
+            return paInvalidChannelCount;
+        }
+    }
+
+    return paNoError;
+}
+
+
+/** Open HPI input stream with given format.
+ This attempts to open HPI input stream with desired format. If the format is not supported
+ or the device is unavailable, the stream is closed and a PortAudio error code is returned.
+
+ @param hostApi Pointer to host API struct
+
+ @param hpiDevice Pointer to HPI device struct
+
+ @param hpiFormat Pointer to HPI format struct
+
+ @return PortAudio error code (typically indicating a problem with stream format or device)
+*/
+static PaError PaAsiHpi_OpenInput( struct PaUtilHostApiRepresentation *hostApi,
+                                   const PaAsiHpiDeviceInfo *hpiDevice, const HPI_FORMAT *hpiFormat,
+                                   HPI_HISTREAM *hpiStream )
+{
+    PaAsiHpiHostApiRepresentation *hpiHostApi = (PaAsiHpiHostApiRepresentation*)hostApi;
+    PaError result = paNoError;
+    HW16 hpiError = 0;
+
+    /* Catch misplaced output devices, as they typically have 0 input channels */
+    PA_UNLESS_( !hpiDevice->streamIsOutput, paInvalidChannelCount );
+    /* Try to open input stream */
+    PA_ASIHPI_UNLESS_( HPI_InStreamOpen( hpiHostApi->subSys, hpiDevice->adapterIndex,
+                                         hpiDevice->streamIndex, hpiStream ), paDeviceUnavailable );
+    /* Set input format (checking it in the process) */
+    /* Could also use HPI_InStreamQueryFormat, but this economizes the process */
+    hpiError = HPI_InStreamSetFormat( hpiHostApi->subSys, *hpiStream, (HPI_FORMAT*)hpiFormat );
+    if( hpiError )
+    {
+        PA_ASIHPI_REPORT_ERROR_( hpiError );
+        PA_ASIHPI_UNLESS_( HPI_InStreamClose( hpiHostApi->subSys, *hpiStream ), paNoError );
+        switch( hpiError )
+        {
+        case HPI_ERROR_INVALID_FORMAT:
+            return paSampleFormatNotSupported;
+
+        case HPI_ERROR_INVALID_SAMPLERATE:
+        case HPI_ERROR_INCOMPATIBLE_SAMPLERATE:
+            return paInvalidSampleRate;
+
+        case HPI_ERROR_INVALID_CHANNELS:
+            return paInvalidChannelCount;
+
+        default:
+            /* In case anything else went wrong */
+            return paInvalidDevice;
+        }
+    }
+
+error:
+    return result;
+}
+
+
+/** Open HPI output stream with given format.
+ This attempts to open HPI output stream with desired format. If the format is not supported
+ or the device is unavailable, the stream is closed and a PortAudio error code is returned.
+
+ @param hostApi Pointer to host API struct
+
+ @param hpiDevice Pointer to HPI device struct
+
+ @param hpiFormat Pointer to HPI format struct
+
+ @return PortAudio error code (typically indicating a problem with stream format or device)
+*/
+static PaError PaAsiHpi_OpenOutput( struct PaUtilHostApiRepresentation *hostApi,
+                                    const PaAsiHpiDeviceInfo *hpiDevice, const HPI_FORMAT *hpiFormat,
+                                    HPI_HOSTREAM *hpiStream )
+{
+    PaAsiHpiHostApiRepresentation *hpiHostApi = (PaAsiHpiHostApiRepresentation*)hostApi;
+    PaError result = paNoError;
+    HW16 hpiError = 0;
+
+    /* Catch misplaced input devices, as they typically have 0 output channels */
+    PA_UNLESS_( hpiDevice->streamIsOutput, paInvalidChannelCount );
+    /* Try to open output stream */
+    PA_ASIHPI_UNLESS_( HPI_OutStreamOpen( hpiHostApi->subSys, hpiDevice->adapterIndex,
+                                          hpiDevice->streamIndex, hpiStream ), paDeviceUnavailable );
+
+    /* Check output format (format is set on first write to output stream) */
+    hpiError = HPI_OutStreamQueryFormat( hpiHostApi->subSys, *hpiStream, (HPI_FORMAT*)hpiFormat );
+    if( hpiError )
+    {
+        PA_ASIHPI_REPORT_ERROR_( hpiError );
+        PA_ASIHPI_UNLESS_( HPI_OutStreamClose( hpiHostApi->subSys, *hpiStream ), paNoError );
+        switch( hpiError )
+        {
+        case HPI_ERROR_INVALID_FORMAT:
+            return paSampleFormatNotSupported;
+
+        case HPI_ERROR_INVALID_SAMPLERATE:
+        case HPI_ERROR_INCOMPATIBLE_SAMPLERATE:
+            return paInvalidSampleRate;
+
+        case HPI_ERROR_INVALID_CHANNELS:
+            return paInvalidChannelCount;
+
+        default:
+            /* In case anything else went wrong */
+            return paInvalidDevice;
+        }
+    }
+
+error:
+    return result;
+}
+
+
+/** Checks whether the desired stream formats and devices are supported
+ (for both input and output).
+ This is done by actually opening the appropriate HPI streams and closing them again.
+
+ @param hostApi Pointer to host API struct
+
+ @param inputParameters Pointer to stream parameter struct for input side of stream
+
+ @param outputParameters Pointer to stream parameter struct for output side of stream
+
+ @param sampleRate Desired sample rate
+
+ @return PortAudio error code (paFormatIsSupported on success)
+ */
+static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
+                                  const PaStreamParameters *inputParameters,
+                                  const PaStreamParameters *outputParameters,
+                                  double sampleRate )
+{
+    PaError result = paFormatIsSupported;
+    PaAsiHpiHostApiRepresentation *hpiHostApi = (PaAsiHpiHostApiRepresentation*)hostApi;
+    PaAsiHpiDeviceInfo *hpiDevice = NULL;
+    HPI_FORMAT hpiFormat;
+
+    /* Input stream */
+    if( inputParameters )
+    {
+        HPI_HISTREAM hpiStream;
+        PA_DEBUG(( "%s: Checking input params: dev=%d, sr=%d, chans=%d, fmt=%d\n",
+                   __FUNCTION__, inputParameters->device, (int)sampleRate,
+                   inputParameters->channelCount, inputParameters->sampleFormat ));
+        /* Create and validate format */
+        PA_ENSURE_( PaAsiHpi_CreateFormat( hostApi, inputParameters, sampleRate,
+                                           &hpiDevice, &hpiFormat ) );
+        /* Open stream to further check format */
+        PA_ENSURE_( PaAsiHpi_OpenInput( hostApi, hpiDevice, &hpiFormat, &hpiStream ) );
+        /* Close stream again */
+        PA_ASIHPI_UNLESS_( HPI_InStreamClose( hpiHostApi->subSys, hpiStream ), paNoError );
+    }
+
+    /* Output stream */
+    if( outputParameters )
+    {
+        HPI_HOSTREAM hpiStream;
+        PA_DEBUG(( "%s: Checking output params: dev=%d, sr=%d, chans=%d, fmt=%d\n",
+                   __FUNCTION__, outputParameters->device, (int)sampleRate,
+                   outputParameters->channelCount, outputParameters->sampleFormat ));
+        /* Create and validate format */
+        PA_ENSURE_( PaAsiHpi_CreateFormat( hostApi, outputParameters, sampleRate,
+                                           &hpiDevice, &hpiFormat ) );
+        /* Open stream to further check format */
+        PA_ENSURE_( PaAsiHpi_OpenOutput( hostApi, hpiDevice, &hpiFormat, &hpiStream ) );
+        /* Close stream again */
+        PA_ASIHPI_UNLESS_( HPI_OutStreamClose( hpiHostApi->subSys, hpiStream ), paNoError );
+    }
+
+error:
+    return result;
+}
+
+/* ---------------------------- Stream Interface ---------------------------- */
+
+/** Obtain HPI stream information.
+ This obtains info such as stream state and available data/space in buffers. It also
+ estimates whether an underflow or overflow occurred.
+
+ @param streamComp Pointer to stream component (input or output) to query
+
+ @param info Pointer to stream info struct that will contain result
+
+ @return PortAudio error code (either paNoError, paDeviceUnavailable or paUnanticipatedHostError)
+ */
+static PaError PaAsiHpi_GetStreamInfo( PaAsiHpiStreamComponent *streamComp, PaAsiHpiStreamInfo *info )
+{
+    PaError result = paDeviceUnavailable;
+    HW16 state;
+    HW32 bufferSize, dataSize, frameCounter, auxDataSize, threshold;
+    HW32 hwBufferSize, hwDataSize;
+
+    assert( streamComp );
+    assert( info );
+
+    /* First blank the stream info struct, in case something goes wrong below.
+       This saves the caller from initializing the struct. */
+    info->state = 0;
+    info->bufferSize = 0;
+    info->dataSize = 0;
+    info->frameCounter = 0;
+    info->auxDataSize = 0;
+    info->totalBufferedData = 0;
+    info->availableFrames = 0;
+    info->underflow = 0;
+    info->overflow = 0;
+
+    if( streamComp->hpiDevice && streamComp->hpiStream )
+    {
+        /* Obtain detailed stream info (either input or output) */
+        if( streamComp->hpiDevice->streamIsOutput )
+        {
+            PA_ASIHPI_UNLESS_( HPI_OutStreamGetInfoEx( streamComp->hpiDevice->subSys,
+                               streamComp->hpiStream,
+                               &state, &bufferSize, &dataSize, &frameCounter,
+                               &auxDataSize ), paUnanticipatedHostError );
+        }
+        else
+        {
+            PA_ASIHPI_UNLESS_( HPI_InStreamGetInfoEx( streamComp->hpiDevice->subSys,
+                               streamComp->hpiStream,
+                               &state, &bufferSize, &dataSize, &frameCounter,
+                               &auxDataSize ), paUnanticipatedHostError );
+        }
+        /* Load stream info */
+        info->state = state;
+        info->bufferSize = bufferSize;
+        info->dataSize = dataSize;
+        info->frameCounter = frameCounter;
+        info->auxDataSize = auxDataSize;
+        /* Determine total buffered data */
+        info->totalBufferedData = dataSize;
+        if( streamComp->hostBufferSize > 0 )
+            info->totalBufferedData += auxDataSize;
+        info->totalBufferedData /= streamComp->bytesPerFrame;
+        /* Determine immediately available frames */
+        info->availableFrames = streamComp->hpiDevice->streamIsOutput ?
+                                bufferSize - dataSize : dataSize;
+        info->availableFrames /= streamComp->bytesPerFrame;
+        /* Minimum space/data required in buffers */
+        threshold = PA_MIN( streamComp->tempBufferSize,
+                            streamComp->bytesPerFrame * PA_ASIHPI_MIN_FRAMES_ );
+        /* Obtain hardware buffer stats first, to simplify things */
+        hwBufferSize = streamComp->hardwareBufferSize;
+        hwDataSize = streamComp->hostBufferSize > 0 ? auxDataSize : dataSize;
+        /* Underflow is a bit tricky */
+        info->underflow = streamComp->hpiDevice->streamIsOutput ?
+                          /* Stream seems to start in drained state sometimes, so ignore initial underflow */
+                          (frameCounter > 0) && ( (state == HPI_STATE_DRAINED) || (hwDataSize == 0) ) :
+                          /* Input streams check the first-level (host) buffer for underflow */
+                          (state != HPI_STATE_STOPPED) && (dataSize < threshold);
+        /* Check for overflow in second-level (hardware) buffer for both input and output */
+        info->overflow = (state != HPI_STATE_STOPPED) && (hwBufferSize - hwDataSize < threshold);
+
+        return paNoError;
+    }
+
+error:
+    return result;
+}
+
+
+/** Display stream component information for debugging purposes.
+
+ @param streamComp Pointer to stream component (input or output) to query
+
+ @param stream Pointer to stream struct which contains the component above
+ */
+static void PaAsiHpi_StreamComponentDump( PaAsiHpiStreamComponent *streamComp,
+        PaAsiHpiStream *stream )
+{
+    PaAsiHpiStreamInfo streamInfo;
+
+    assert( streamComp );
+    assert( stream );
+
+    /* Name of soundcard/device used by component */
+    PA_DEBUG(( "device: %s\n", streamComp->hpiDevice->baseDeviceInfo.name ));
+    /* Unfortunately some overlap between input and output here */
+    if( streamComp->hpiDevice->streamIsOutput )
+    {
+        /* Settings on the user side (as experienced by user callback) */
+        PA_DEBUG(( "user: %d-bit, %d ",
+                   8*stream->bufferProcessor.bytesPerUserOutputSample,
+                   stream->bufferProcessor.outputChannelCount));
+        if( stream->bufferProcessor.userOutputIsInterleaved )
+        {
+            PA_DEBUG(( "interleaved channels, " ));
+        }
+        else
+        {
+            PA_DEBUG(( "non-interleaved channels, " ));
+        }
+        PA_DEBUG(( "%d frames/buffer, latency = %5.1f ms\n",
+                   stream->bufferProcessor.framesPerUserBuffer,
+                   1000*stream->baseStreamRep.streamInfo.outputLatency ));
+        /* Settings on the host side (internal to PortAudio host API) */
+        PA_DEBUG(( "host: %d-bit, %d interleaved channels, %d frames/buffer ",
+                   8*stream->bufferProcessor.bytesPerHostOutputSample,
+                   stream->bufferProcessor.outputChannelCount,
+                   stream->bufferProcessor.framesPerHostBuffer ));
+    }
+    else
+    {
+        /* Settings on the user side (as experienced by user callback) */
+        PA_DEBUG(( "user: %d-bit, %d ",
+                   8*stream->bufferProcessor.bytesPerUserInputSample,
+                   stream->bufferProcessor.inputChannelCount));
+        if( stream->bufferProcessor.userInputIsInterleaved )
+        {
+            PA_DEBUG(( "interleaved channels, " ));
+        }
+        else
+        {
+            PA_DEBUG(( "non-interleaved channels, " ));
+        }
+        PA_DEBUG(( "%d frames/buffer, latency = %5.1f ms\n",
+                   stream->bufferProcessor.framesPerUserBuffer,
+                   1000*stream->baseStreamRep.streamInfo.inputLatency ));
+        /* Settings on the host side (internal to PortAudio host API) */
+        PA_DEBUG(( "host: %d-bit, %d interleaved channels, %d frames/buffer ",
+                   8*stream->bufferProcessor.bytesPerHostInputSample,
+                   stream->bufferProcessor.inputChannelCount,
+                   stream->bufferProcessor.framesPerHostBuffer ));
+    }
+    switch( stream->bufferProcessor.hostBufferSizeMode )
+    {
+    case paUtilFixedHostBufferSize:
+        PA_DEBUG(( "[fixed] " ));
+        break;
+    case paUtilBoundedHostBufferSize:
+        PA_DEBUG(( "[bounded] " ));
+        break;
+    case paUtilUnknownHostBufferSize:
+        PA_DEBUG(( "[unknown] " ));
+        break;
+    case paUtilVariableHostBufferSizePartialUsageAllowed:
+        PA_DEBUG(( "[variable] " ));
+        break;
+    }
+    PA_DEBUG(( "(%d max)\n", streamComp->tempBufferSize / streamComp->bytesPerFrame ));
+    /* HPI hardware settings */
+    PA_DEBUG(( "HPI: adapter %d stream %d, %d-bit, %d-channel, %d Hz\n",
+               streamComp->hpiDevice->adapterIndex, streamComp->hpiDevice->streamIndex,
+               8 * streamComp->bytesPerFrame / streamComp->hpiFormat.wChannels,
+               streamComp->hpiFormat.wChannels,
+               streamComp->hpiFormat.dwSampleRate ));
+    /* Stream state and buffer levels */
+    PA_DEBUG(( "HPI: " ));
+    PaAsiHpi_GetStreamInfo( streamComp, &streamInfo );
+    switch( streamInfo.state )
+    {
+    case HPI_STATE_STOPPED:
+        PA_DEBUG(( "[STOPPED] " ));
+        break;
+    case HPI_STATE_PLAYING:
+        PA_DEBUG(( "[PLAYING] " ));
+        break;
+    case HPI_STATE_RECORDING:
+        PA_DEBUG(( "[RECORDING] " ));
+        break;
+    case HPI_STATE_DRAINED:
+        PA_DEBUG(( "[DRAINED] " ));
+        break;
+    default:
+        PA_DEBUG(( "[unknown state] " ));
+        break;
+    }
+    if( streamComp->hostBufferSize )
+    {
+        PA_DEBUG(( "host = %d/%d B, ", streamInfo.dataSize, streamComp->hostBufferSize ));
+        PA_DEBUG(( "hw = %d/%d (%d) B, ", streamInfo.auxDataSize,
+                   streamComp->hardwareBufferSize, streamComp->outputBufferCap ));
+    }
+    else
+    {
+        PA_DEBUG(( "hw = %d/%d B, ", streamInfo.dataSize, streamComp->hardwareBufferSize ));
+    }
+    PA_DEBUG(( "count = %d", streamInfo.frameCounter ));
+    if( streamInfo.overflow )
+    {
+        PA_DEBUG(( " [overflow]" ));
+    }
+    else if( streamInfo.underflow )
+    {
+        PA_DEBUG(( " [underflow]" ));
+    }
+    PA_DEBUG(( "\n" ));
+}
+
+
+/** Display stream information for debugging purposes.
+
+ @param stream Pointer to stream to query
+ */
+static void PaAsiHpi_StreamDump( PaAsiHpiStream *stream )
+{
+    assert( stream );
+
+    PA_DEBUG(( "\n------------------------- STREAM INFO FOR %p ---------------------------\n", stream ));
+    /* General stream info (input+output) */
+    if( stream->baseStreamRep.streamCallback )
+    {
+        PA_DEBUG(( "[callback] " ));
+    }
+    else
+    {
+        PA_DEBUG(( "[blocking] " ));
+    }
+    PA_DEBUG(( "sr=%d Hz, poll=%d ms, max %d frames/buf ",
+               (int)stream->baseStreamRep.streamInfo.sampleRate,
+               stream->pollingInterval, stream->maxFramesPerHostBuffer ));
+    switch( stream->state )
+    {
+    case paAsiHpiStoppedState:
+        PA_DEBUG(( "[stopped]\n" ));
+        break;
+    case paAsiHpiActiveState:
+        PA_DEBUG(( "[active]\n" ));
+        break;
+    case paAsiHpiCallbackFinishedState:
+        PA_DEBUG(( "[cb fin]\n" ));
+        break;
+    default:
+        PA_DEBUG(( "[unknown state]\n" ));
+        break;
+    }
+    if( stream->callbackMode )
+    {
+        PA_DEBUG(( "cb info: thread=%p, cbAbort=%d, cbFinished=%d\n",
+                   stream->thread.thread, stream->callbackAbort, stream->callbackFinished ));
+    }
+
+    PA_DEBUG(( "----------------------------------- Input  ------------------------------------\n" ));
+    if( stream->input )
+    {
+        PaAsiHpi_StreamComponentDump( stream->input, stream );
+    }
+    else
+    {
+        PA_DEBUG(( "*none*\n" ));
+    }
+
+    PA_DEBUG(( "----------------------------------- Output ------------------------------------\n" ));
+    if( stream->output )
+    {
+        PaAsiHpi_StreamComponentDump( stream->output, stream );
+    }
+    else
+    {
+        PA_DEBUG(( "*none*\n" ));
+    }
+    PA_DEBUG(( "-------------------------------------------------------------------------------\n\n" ));
+
+}
+
+
+/** Determine buffer sizes and allocate appropriate stream buffers.
+ This attempts to allocate a BBM (host) buffer for the HPI stream component (either input
+ or output, as both have similar buffer needs). Not all AudioScience adapters support BBM,
+ in which case the hardware buffer has to suffice. The size of the HPI host buffer is chosen
+ as a multiple of framesPerPaHostBuffer, and also influenced by the suggested latency and the
+ estimated minimum polling interval. The HPI host and hardware buffer sizes are stored, and an
+ appropriate cap for the hardware buffer is also calculated. Finally, the temporary stream
+ buffer which serves as the PortAudio host buffer for this implementation is allocated.
+ This buffer contains an integer number of user buffers, to simplify buffer adaption in the
+ buffer processor. The function returns paBufferTooBig if the HPI interface cannot allocate
+ an HPI host buffer of the desired size.
+
+ @param streamComp Pointer to stream component struct
+
+ @param pollingInterval Polling interval for stream, in milliseconds
+
+ @param framesPerPaHostBuffer Size of PortAudio host buffer, in frames
+
+ @param suggestedLatency Suggested latency for stream component, in seconds
+
+ @return PortAudio error code (possibly paBufferTooBig or paInsufficientMemory)
+ */
+static PaError PaAsiHpi_SetupBuffers( PaAsiHpiStreamComponent *streamComp, HW32 pollingInterval,
+                                      unsigned long framesPerPaHostBuffer, PaTime suggestedLatency )
+{
+    PaError result = paNoError;
+    PaAsiHpiStreamInfo streamInfo;
+    unsigned long hpiBufferSize = 0, paHostBufferSize = 0;
+
+    assert( streamComp );
+    assert( streamComp->hpiDevice );
+
+    /* Obtain size of hardware buffer of HPI stream, since we will be activating BBM shortly
+       and afterwards the buffer size will refer to the BBM (host-side) buffer.
+       This is necessary to enable reliable detection of xruns. */
+    PA_ENSURE_( PaAsiHpi_GetStreamInfo( streamComp, &streamInfo ) );
+    streamComp->hardwareBufferSize = streamInfo.bufferSize;
+    hpiBufferSize = streamInfo.bufferSize;
+
+    /* Check if BBM (background bus mastering) is to be enabled */
+    if( PA_ASIHPI_USE_BBM_ )
+    {
+        HW32 bbmBufferSize = 0, preLatencyBufferSize = 0;
+        HW16 hpiError = 0;
+        PaTime pollingOverhead;
+
+        /* Check overhead of Pa_Sleep() call (minimum sleep duration in ms -> OS dependent) */
+        pollingOverhead = PaUtil_GetTime();
+        Pa_Sleep( 0 );
+        pollingOverhead = 1000*(PaUtil_GetTime() - pollingOverhead);
+        PA_DEBUG(( "polling overhead = %f ms (length of 0-second sleep)\n", pollingOverhead ));
+        /* Obtain minimum recommended size for host buffer (in bytes) */
+        PA_ASIHPI_UNLESS_( HPI_StreamEstimateBufferSize( &streamComp->hpiFormat,
+                           pollingInterval + (HW32)ceil( pollingOverhead ),
+                           &bbmBufferSize ), paUnanticipatedHostError );
+        /* BBM places more stringent requirements on buffer size (see description */
+        /* of HPI_StreamEstimateBufferSize in HPI API document) */
+        bbmBufferSize *= 3;
+        /* Make sure the BBM buffer contains multiple PA host buffers */
+        if( bbmBufferSize < 3 * streamComp->bytesPerFrame * framesPerPaHostBuffer )
+            bbmBufferSize = 3 * streamComp->bytesPerFrame * framesPerPaHostBuffer;
+        /* Try to honor latency suggested by user by growing buffer (no decrease possible) */
+        if( suggestedLatency > 0.0 )
+        {
+            PaTime bufferDuration = ((PaTime)bbmBufferSize) / streamComp->bytesPerFrame
+                                    / streamComp->hpiFormat.dwSampleRate;
+            /* Don't decrease buffer */
+            if( bufferDuration < suggestedLatency )
+            {
+                /* Save old buffer size, to be retried if new size proves too big */
+                preLatencyBufferSize = bbmBufferSize;
+                bbmBufferSize = (HW32)ceil( suggestedLatency * streamComp->bytesPerFrame
+                                            * streamComp->hpiFormat.dwSampleRate );
+            }
+        }
+        /* Choose closest memory block boundary (HPI API document states that
+        "a buffer size of Nx4096 - 20 makes the best use of memory"
+        (under the entry for HPI_StreamEstimateBufferSize)) */
+        bbmBufferSize = ((HW32)ceil((bbmBufferSize + 20)/4096.0))*4096 - 20;
+        streamComp->hostBufferSize = bbmBufferSize;
+        /* Allocate BBM host buffer (this enables bus mastering transfers in background) */
+        if( streamComp->hpiDevice->streamIsOutput )
+            hpiError = HPI_OutStreamHostBufferAllocate( streamComp->hpiDevice->subSys,
+                       streamComp->hpiStream,
+                       bbmBufferSize );
+        else
+            hpiError = HPI_InStreamHostBufferAllocate( streamComp->hpiDevice->subSys,
+                       streamComp->hpiStream,
+                       bbmBufferSize );
+        if( hpiError )
+        {
+            PA_ASIHPI_REPORT_ERROR_( hpiError );
+            /* Indicate that BBM is disabled */
+            streamComp->hostBufferSize = 0;
+            /* Retry with smaller buffer size (transfers will still work, but not via BBM) */
+            if( hpiError == HPI_ERROR_INVALID_DATASIZE )
+            {
+                /* Retry BBM allocation with smaller size if requested latency proved too big */
+                if( preLatencyBufferSize > 0 )
+                {
+                    PA_DEBUG(( "Retrying BBM allocation with smaller size (%d vs. %d bytes)\n",
+                               preLatencyBufferSize, bbmBufferSize ));
+                    bbmBufferSize = preLatencyBufferSize;
+                    if( streamComp->hpiDevice->streamIsOutput )
+                        hpiError = HPI_OutStreamHostBufferAllocate( streamComp->hpiDevice->subSys,
+                                   streamComp->hpiStream,
+                                   bbmBufferSize );
+                    else
+                        hpiError = HPI_InStreamHostBufferAllocate( streamComp->hpiDevice->subSys,
+                                   streamComp->hpiStream,
+                                   bbmBufferSize );
+                    /* Another round of error checking */
+                    if( hpiError )
+                    {
+                        PA_ASIHPI_REPORT_ERROR_( hpiError );
+                        /* No escapes this time */
+                        if( hpiError == HPI_ERROR_INVALID_DATASIZE )
+                        {
+                            result = paBufferTooBig;
+                            goto error;
+                        }
+                        else if( hpiError != HPI_ERROR_INVALID_OPERATION )
+                        {
+                            result = paUnanticipatedHostError;
+                            goto error;
+                        }
+                    }
+                    else
+                    {
+                        streamComp->hostBufferSize = bbmBufferSize;
+                        hpiBufferSize = bbmBufferSize;
+                    }
+                }
+                else
+                {
+                    result = paBufferTooBig;
+                    goto error;
+                }
+            }
+            /* If BBM not supported, foreground transfers will be used, but not a show-stopper */
+            /* Anything else is an error */
+            else if( hpiError != HPI_ERROR_INVALID_OPERATION )
+            {
+                result = paUnanticipatedHostError;
+                goto error;
+            }
+        }
+        else
+        {
+            hpiBufferSize = bbmBufferSize;
+        }
+    }
+
+    /* Final check of buffer size */
+    paHostBufferSize = streamComp->bytesPerFrame * framesPerPaHostBuffer;
+    if( hpiBufferSize < 3*paHostBufferSize )
+    {
+        result = paBufferTooBig;
+        goto error;
+    }
+    /* Set cap on output buffer size, based on latency suggestions */
+    if( streamComp->hpiDevice->streamIsOutput )
+    {
+        PaTime latency = suggestedLatency > 0.0 ? suggestedLatency :
+                         streamComp->hpiDevice->baseDeviceInfo.defaultHighOutputLatency;
+        streamComp->outputBufferCap =
+            (HW32)ceil( latency * streamComp->bytesPerFrame * streamComp->hpiFormat.dwSampleRate );
+        /* The cap should not be too small, to prevent underflow */
+        if( streamComp->outputBufferCap < 4*paHostBufferSize )
+            streamComp->outputBufferCap = 4*paHostBufferSize;
+    }
+    else
+    {
+        streamComp->outputBufferCap = 0;
+    }
+    /* Temp buffer size should be multiple of PA host buffer size (or 1x, if using fixed blocks) */
+    streamComp->tempBufferSize = paHostBufferSize;
+    /* Allocate temp buffer */
+    PA_UNLESS_( streamComp->tempBuffer = (HW8 *)PaUtil_AllocateMemory( streamComp->tempBufferSize ),
+                paInsufficientMemory );
+error:
+    return result;
+}
+
+
+/** Opens PortAudio stream.
+ This determines a suitable value for framesPerBuffer, if the user didn't specify it,
+ based on the suggested latency. It then opens each requested stream direction with the
+ appropriate stream format, and allocates the required stream buffers. It sets up the
+ various PortAudio structures dealing with streams, and estimates the stream latency.
+
+ See pa_hostapi.h for a list of validity guarantees made about OpenStream parameters.
+
+ @param hostApi Pointer to host API struct
+
+ @param s List of open streams, where successfully opened stream will go
+
+ @param inputParameters Pointer to stream parameter struct for input side of stream
+
+ @param outputParameters Pointer to stream parameter struct for output side of stream
+
+ @param sampleRate Desired sample rate
+
+ @param framesPerBuffer Desired number of frames per buffer passed to user callback
+                        (or chunk size for blocking stream)
+
+ @param streamFlags Stream flags
+
+ @param streamCallback Pointer to user callback function (zero for blocking interface)
+
+ @param userData Pointer to user data that will be passed to callback function along with data
+
+ @return PortAudio error code
+*/
+static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
+                           PaStream **s,
+                           const PaStreamParameters *inputParameters,
+                           const PaStreamParameters *outputParameters,
+                           double sampleRate,
+                           unsigned long framesPerBuffer,
+                           PaStreamFlags streamFlags,
+                           PaStreamCallback *streamCallback,
+                           void *userData )
+{
+    PaError result = paNoError;
+    PaAsiHpiHostApiRepresentation *hpiHostApi = (PaAsiHpiHostApiRepresentation*)hostApi;
+    PaAsiHpiStream *stream = NULL;
+    unsigned long framesPerHostBuffer = framesPerBuffer;
+    int inputChannelCount = 0, outputChannelCount = 0;
+    PaSampleFormat inputSampleFormat = 0, outputSampleFormat = 0;
+    PaSampleFormat hostInputSampleFormat = 0, hostOutputSampleFormat = 0;
+    PaTime maxSuggestedLatency = 0.0;
+
+    /* Validate platform-specific flags -> none expected for HPI */
+    if( (streamFlags & paPlatformSpecificFlags) != 0 )
+        return paInvalidFlag; /* unexpected platform-specific flag */
+
+    /* Create blank stream structure */
+    PA_UNLESS_( stream = (PaAsiHpiStream *)PaUtil_AllocateMemory( sizeof(PaAsiHpiStream) ),
+                paInsufficientMemory );
+    memset( stream, 0, sizeof(PaAsiHpiStream) );
+
+    /* If the number of frames per buffer is unspecified, we have to come up with one. */
+    if( framesPerHostBuffer == paFramesPerBufferUnspecified )
+    {
+        if( inputParameters )
+            maxSuggestedLatency = inputParameters->suggestedLatency;
+        if( outputParameters && (outputParameters->suggestedLatency > maxSuggestedLatency) )
+            maxSuggestedLatency = outputParameters->suggestedLatency;
+        /* Use suggested latency if available */
+        if( maxSuggestedLatency > 0.0 )
+            framesPerHostBuffer = (unsigned long)ceil( maxSuggestedLatency * sampleRate );
+        else
+            /* AudioScience cards like BIG buffers by default */
+            framesPerHostBuffer = 4096;
+    }
+    /* Lower bounds on host buffer size, due to polling and HPI constraints */
+    if( 1000.0*framesPerHostBuffer/sampleRate < PA_ASIHPI_MIN_POLLING_INTERVAL_ )
+        framesPerHostBuffer = (unsigned long)ceil( sampleRate * PA_ASIHPI_MIN_POLLING_INTERVAL_ / 1000.0 );
+    /*    if( framesPerHostBuffer < PA_ASIHPI_MIN_FRAMES_ )
+            framesPerHostBuffer = PA_ASIHPI_MIN_FRAMES_; */
+    /* Efficient if host buffer size is integer multiple of user buffer size */
+    if( framesPerBuffer > 0 )
+        framesPerHostBuffer = (unsigned long)ceil( (double)framesPerHostBuffer / framesPerBuffer ) * framesPerBuffer;
+    /* Buffer should always be a multiple of 4 bytes to facilitate 32-bit PCI transfers.
+     By keeping the frames a multiple of 4, this is ensured even for 8-bit mono sound. */
+    framesPerHostBuffer = (framesPerHostBuffer / 4) * 4;
+    /* Polling is based on time length (in milliseconds) of user-requested block size */
+    stream->pollingInterval = (HW32)ceil( 1000.0*framesPerHostBuffer/sampleRate );
+    assert( framesPerHostBuffer > 0 );
+
+    /* Open underlying streams, check formats and allocate buffers */
+    if( inputParameters )
+    {
+        /* Create blank stream component structure */
+        PA_UNLESS_( stream->input = (PaAsiHpiStreamComponent *)PaUtil_AllocateMemory( sizeof(PaAsiHpiStreamComponent) ),
+                    paInsufficientMemory );
+        memset( stream->input, 0, sizeof(PaAsiHpiStreamComponent) );
+        /* Create/validate format */
+        PA_ENSURE_( PaAsiHpi_CreateFormat( hostApi, inputParameters, sampleRate,
+                                           &stream->input->hpiDevice, &stream->input->hpiFormat ) );
+        /* Open stream and set format */
+        PA_ENSURE_( PaAsiHpi_OpenInput( hostApi, stream->input->hpiDevice, &stream->input->hpiFormat,
+                                        &stream->input->hpiStream ) );
+        inputChannelCount = inputParameters->channelCount;
+        inputSampleFormat = inputParameters->sampleFormat;
+        hostInputSampleFormat = PaAsiHpi_HpiToPaFormat( stream->input->hpiFormat.wFormat );
+        stream->input->bytesPerFrame = inputChannelCount * Pa_GetSampleSize( hostInputSampleFormat );
+        assert( stream->input->bytesPerFrame > 0 );
+        /* Allocate host and temp buffers of appropriate size */
+        PA_ENSURE_( PaAsiHpi_SetupBuffers( stream->input, stream->pollingInterval,
+                                           framesPerHostBuffer, inputParameters->suggestedLatency ) );
+    }
+    if( outputParameters )
+    {
+        /* Create blank stream component structure */
+        PA_UNLESS_( stream->output = (PaAsiHpiStreamComponent *)PaUtil_AllocateMemory( sizeof(PaAsiHpiStreamComponent) ),
+                    paInsufficientMemory );
+        memset( stream->output, 0, sizeof(PaAsiHpiStreamComponent) );
+        /* Create/validate format */
+        PA_ENSURE_( PaAsiHpi_CreateFormat( hostApi, outputParameters, sampleRate,
+                                           &stream->output->hpiDevice, &stream->output->hpiFormat ) );
+        /* Open stream and check format */
+        PA_ENSURE_( PaAsiHpi_OpenOutput( hostApi, stream->output->hpiDevice,
+                                         &stream->output->hpiFormat,
+                                         &stream->output->hpiStream ) );
+        outputChannelCount = outputParameters->channelCount;
+        outputSampleFormat = outputParameters->sampleFormat;
+        hostOutputSampleFormat = PaAsiHpi_HpiToPaFormat( stream->output->hpiFormat.wFormat );
+        stream->output->bytesPerFrame = outputChannelCount * Pa_GetSampleSize( hostOutputSampleFormat );
+        /* Allocate host and temp buffers of appropriate size */
+        PA_ENSURE_( PaAsiHpi_SetupBuffers( stream->output, stream->pollingInterval,
+                                           framesPerHostBuffer, outputParameters->suggestedLatency ) );
+    }
+
+    /* Determine maximum frames per host buffer (least common denominator of input/output) */
+    if( inputParameters && outputParameters )
+    {
+        stream->maxFramesPerHostBuffer = PA_MIN( stream->input->tempBufferSize / stream->input->bytesPerFrame,
+                                         stream->output->tempBufferSize / stream->output->bytesPerFrame );
+    }
+    else
+    {
+        stream->maxFramesPerHostBuffer = inputParameters ? stream->input->tempBufferSize / stream->input->bytesPerFrame
+                                         : stream->output->tempBufferSize / stream->output->bytesPerFrame;
+    }
+    assert( stream->maxFramesPerHostBuffer > 0 );
+    /* Initialize various other stream parameters */
+    stream->neverDropInput = streamFlags & paNeverDropInput;
+    stream->state = paAsiHpiStoppedState;
+
+    /* Initialize either callback or blocking interface */
+    if( streamCallback )
+    {
+        PaUtil_InitializeStreamRepresentation( &stream->baseStreamRep,
+                                               &hpiHostApi->callbackStreamInterface,
+                                               streamCallback, userData );
+        stream->callbackMode = 1;
+    }
+    else
+    {
+        PaUtil_InitializeStreamRepresentation( &stream->baseStreamRep,
+                                               &hpiHostApi->blockingStreamInterface,
+                                               streamCallback, userData );
+        /* Pre-allocate non-interleaved user buffer pointers for blocking interface */
+        PA_UNLESS_( stream->blockingUserBufferCopy =
+                        PaUtil_AllocateMemory( sizeof(void *) * PA_MAX( inputChannelCount, outputChannelCount ) ),
+                    paInsufficientMemory );
+        stream->callbackMode = 0;
+    }
+    PaUtil_InitializeCpuLoadMeasurer( &stream->cpuLoadMeasurer, sampleRate );
+
+    /* Following pa_linux_alsa's lead, we operate with fixed host buffer size by default, */
+    /* since other modes will invariably lead to block adaption (maybe Bounded better?) */
+    PA_ENSURE_( PaUtil_InitializeBufferProcessor( &stream->bufferProcessor,
+                inputChannelCount, inputSampleFormat, hostInputSampleFormat,
+                outputChannelCount, outputSampleFormat, hostOutputSampleFormat,
+                sampleRate, streamFlags,
+                framesPerBuffer, framesPerHostBuffer, paUtilFixedHostBufferSize,
+                streamCallback, userData ) );
+
+    stream->baseStreamRep.streamInfo.structVersion = 1;
+    stream->baseStreamRep.streamInfo.sampleRate = sampleRate;
+    /* Determine input latency from buffer processor and buffer sizes */
+    if( stream->input )
+    {
+        PaTime bufferDuration = ( stream->input->hostBufferSize + stream->input->hardwareBufferSize )
+                                / sampleRate / stream->input->bytesPerFrame;
+        stream->baseStreamRep.streamInfo.inputLatency =
+            PaUtil_GetBufferProcessorInputLatency( &stream->bufferProcessor ) +
+            bufferDuration - stream->maxFramesPerHostBuffer / sampleRate;
+        assert( stream->baseStreamRep.streamInfo.inputLatency > 0.0 );
+    }
+    /* Determine output latency from buffer processor and buffer sizes */
+    if( stream->output )
+    {
+        PaTime bufferDuration = ( stream->output->hostBufferSize + stream->output->hardwareBufferSize )
+                                / sampleRate / stream->output->bytesPerFrame;
+        /* Take buffer size cap into account (see PaAsiHpi_WaitForFrames) */
+        if( !stream->input && (stream->output->outputBufferCap > 0) )
+        {
+            bufferDuration = PA_MIN( bufferDuration,
+                                     stream->output->outputBufferCap / sampleRate / stream->output->bytesPerFrame );
+        }
+        stream->baseStreamRep.streamInfo.outputLatency =
+            PaUtil_GetBufferProcessorOutputLatency( &stream->bufferProcessor ) +
+            bufferDuration - stream->maxFramesPerHostBuffer / sampleRate;
+        assert( stream->baseStreamRep.streamInfo.outputLatency > 0.0 );
+    }
+
+    /* Report stream info, for debugging purposes */
+    PaAsiHpi_StreamDump( stream );
+
+    /* Save initialized stream to PA stream list */
+    *s = (PaStream*)stream;
+    return result;
+
+error:
+    CloseStream( (PaStream*)stream );
+    return result;
+}
+
+
+/** Close PortAudio stream.
+ When CloseStream() is called, the multi-api layer ensures that the stream has already
+ been stopped or aborted. This closes the underlying HPI streams and deallocates stream
+ buffers and structs.
+
+ @param s Pointer to PortAudio stream
+
+ @return PortAudio error code
+*/
+static PaError CloseStream( PaStream *s )
+{
+    PaError result = paNoError;
+    PaAsiHpiStream *stream = (PaAsiHpiStream*)s;
+
+    /* If stream is already gone, all is well */
+    if( stream == NULL )
+        return paNoError;
+
+    /* Generic stream cleanup */
+    PaUtil_TerminateBufferProcessor( &stream->bufferProcessor );
+    PaUtil_TerminateStreamRepresentation( &stream->baseStreamRep );
+
+    /* Implementation-specific details - close internal streams */
+    if( stream->input )
+    {
+        /* Close HPI stream (freeing BBM host buffer in the process, if used) */
+        if( stream->input->hpiStream )
+        {
+            PA_ASIHPI_UNLESS_( HPI_InStreamClose( stream->input->hpiDevice->subSys,
+                                                  stream->input->hpiStream ), paUnanticipatedHostError );
+        }
+        /* Free temp buffer and stream component */
+        PaUtil_FreeMemory( stream->input->tempBuffer );
+        PaUtil_FreeMemory( stream->input );
+    }
+    if( stream->output )
+    {
+        /* Close HPI stream (freeing BBM host buffer in the process, if used) */
+        if( stream->output->hpiStream )
+        {
+            PA_ASIHPI_UNLESS_( HPI_OutStreamClose( stream->output->hpiDevice->subSys,
+                                                   stream->output->hpiStream ), paUnanticipatedHostError );
+        }
+        /* Free temp buffer and stream component */
+        PaUtil_FreeMemory( stream->output->tempBuffer );
+        PaUtil_FreeMemory( stream->output );
+    }
+
+    PaUtil_FreeMemory( stream->blockingUserBufferCopy );
+    PaUtil_FreeMemory( stream );
+
+error:
+    return result;
+}
+
+
+/** Prime HPI output stream with silence.
+ This resets the output stream and uses PortAudio helper routines to fill the
+ temp buffer with silence. It then writes two host buffers to the stream. This is supposed
+ to be called before the stream is started. It has no effect on input-only streams.
+
+ @param stream Pointer to stream struct
+
+ @return PortAudio error code
+ */
+static PaError PaAsiHpi_PrimeOutputWithSilence( PaAsiHpiStream *stream )
+{
+    PaError result = paNoError;
+    PaAsiHpiStreamComponent *out;
+    PaUtilZeroer *zeroer;
+    PaSampleFormat outputFormat;
+    HPI_DATA data;
+
+    assert( stream );
+    out = stream->output;
+    /* Only continue if stream has output channels */
+    if( !out )
+        return result;
+    assert( out->tempBuffer );
+
+    /* Clear all existing data in hardware playback buffer */
+    PA_ASIHPI_UNLESS_( HPI_OutStreamReset( out->hpiDevice->subSys,
+                                           out->hpiStream ), paUnanticipatedHostError );
+    /* Fill temp buffer with silence */
+    outputFormat = PaAsiHpi_HpiToPaFormat( out->hpiFormat.wFormat );
+    zeroer = PaUtil_SelectZeroer( outputFormat );
+    zeroer(out->tempBuffer, 1, out->tempBufferSize / Pa_GetSampleSize(outputFormat) );
+    /* Write temp buffer to hardware fifo twice, to get started */
+    PA_ASIHPI_UNLESS_( HPI_DataCreate( &data, &out->hpiFormat, out->tempBuffer, out->tempBufferSize ),
+                       paUnanticipatedHostError );
+    PA_ASIHPI_UNLESS_( HPI_OutStreamWrite( out->hpiDevice->subSys,
+                                           out->hpiStream, &data ), paUnanticipatedHostError );
+    PA_ASIHPI_UNLESS_( HPI_OutStreamWrite( out->hpiDevice->subSys,
+                                           out->hpiStream, &data ), paUnanticipatedHostError );
+
+error:
+    return result;
+}
+
+
+/** Start HPI streams (both input + output).
+ This starts all HPI streams in the PortAudio stream. Output streams are first primed with
+ silence, if required. After this call the PA stream is in the Active state.
+
+ @todo Implement priming via the user callback
+
+ @param stream Pointer to stream struct
+
+ @param outputPrimed True if output is already primed (if false, silence will be loaded before starting)
+
+ @return PortAudio error code
+ */
+static PaError PaAsiHpi_StartStream( PaAsiHpiStream *stream, int outputPrimed )
+{
+    PaError result = paNoError;
+
+    if( stream->input )
+    {
+        PA_ASIHPI_UNLESS_( HPI_InStreamStart( stream->input->hpiDevice->subSys,
+                                              stream->input->hpiStream ), paUnanticipatedHostError );
+    }
+    if( stream->output )
+    {
+        if( !outputPrimed )
+        {
+            /* Buffer isn't primed, so load stream with silence */
+            PA_ENSURE_( PaAsiHpi_PrimeOutputWithSilence( stream ) );
+        }
+        PA_ASIHPI_UNLESS_( HPI_OutStreamStart( stream->output->hpiDevice->subSys,
+                                               stream->output->hpiStream ), paUnanticipatedHostError );
+    }
+    stream->state = paAsiHpiActiveState;
+    stream->callbackFinished = 0;
+
+    /* Report stream info for debugging purposes */
+    /*    PaAsiHpi_StreamDump( stream );   */
+
+error:
+    return result;
+}
+
+
+/** Start PortAudio stream.
+ If the stream has a callback interface, this starts a helper thread to feed the user callback.
+ The thread will then take care of starting the HPI streams, and this function will block
+ until the streams actually start. In the case of a blocking interface, the HPI streams
+ are simply started.
+
+ @param s Pointer to PortAudio stream
+
+ @return PortAudio error code
+*/
+static PaError StartStream( PaStream *s )
+{
+    PaError result = paNoError;
+    PaAsiHpiStream *stream = (PaAsiHpiStream*)s;
+
+    assert( stream );
+
+    /* Ready the processor */
+    PaUtil_ResetBufferProcessor( &stream->bufferProcessor );
+
+    if( stream->callbackMode )
+    {
+        /* Create and start callback engine thread */
+        /* Also waits 1 second for stream to be started by engine thread (otherwise aborts) */
+        PA_ENSURE_( PaUnixThread_New( &stream->thread, &CallbackThreadFunc, stream, 1., 0 ) );
+    }
+    else
+    {
+        PA_ENSURE_( PaAsiHpi_StartStream( stream, 0 ) );
+    }
+
+error:
+    return result;
+}
+
+
+/** Stop HPI streams (input + output), either softly or abruptly.
+ If abort is false, the function blocks until the output stream is drained, otherwise it
+ stops immediately and discards data in the stream hardware buffers.
+
+ This function is safe to call from the callback engine thread as well as the main thread.
+
+ @param stream Pointer to stream struct
+
+ @param abort True if samples in output buffer should be discarded (otherwise blocks until stream is done)
+
+ @return PortAudio error code
+
+ */
+static PaError PaAsiHpi_StopStream( PaAsiHpiStream *stream, int abort )
+{
+    PaError result = paNoError;
+
+    assert( stream );
+
+    /* Input channels */
+    if( stream->input )
+    {
+        PA_ASIHPI_UNLESS_( HPI_InStreamReset( stream->input->hpiDevice->subSys,
+                                              stream->input->hpiStream ), paUnanticipatedHostError );
+    }
+    /* Output channels */
+    if( stream->output )
+    {
+        if( !abort )
+        {
+            /* Wait until HPI output stream is drained */
+            while( 1 )
+            {
+                PaAsiHpiStreamInfo streamInfo;
+                PaTime timeLeft;
+
+                /* Obtain number of samples waiting to be played */
+                PA_ENSURE_( PaAsiHpi_GetStreamInfo( stream->output, &streamInfo ) );
+                /* Check if stream is drained */
+                if( (streamInfo.state != HPI_STATE_PLAYING) &&
+                        (streamInfo.dataSize < stream->output->bytesPerFrame * PA_ASIHPI_MIN_FRAMES_) )
+                    break;
+                /* Sleep amount of time represented by remaining samples */
+                timeLeft = 1000.0 * streamInfo.dataSize / stream->output->bytesPerFrame
+                           / stream->baseStreamRep.streamInfo.sampleRate;
+                Pa_Sleep( (long)ceil( timeLeft ) );
+            }
+        }
+        PA_ASIHPI_UNLESS_( HPI_OutStreamReset( stream->output->hpiDevice->subSys,
+                                               stream->output->hpiStream ), paUnanticipatedHostError );
+    }
+
+    /* Report stream info for debugging purposes */
+    /*    PaAsiHpi_StreamDump( stream ); */
+
+error:
+    return result;
+}
+
+
+/** Stop or abort PortAudio stream.
+
+ This function is used to explicitly stop the PortAudio stream (via StopStream/AbortStream),
+ as opposed to the situation when the callback finishes with a result other than paContinue.
+ If a stream is in callback mode we will have to inspect whether the background thread has
+ finished, or we will have to take it out. In either case we join the thread before returning.
+ In blocking mode, we simply tell HPI to stop abruptly (abort) or finish buffers (drain).
+ The PortAudio stream will be in the Stopped state after a call to this function.
+
+ Don't call this from the callback engine thread!
+
+ @param stream Pointer to stream struct
+
+ @param abort True if samples in output buffer should be discarded (otherwise blocks until stream is done)
+
+ @return PortAudio error code
+*/
+static PaError PaAsiHpi_ExplicitStop( PaAsiHpiStream *stream, int abort )
+{
+    PaError result = paNoError;
+
+    /* First deal with the callback thread, cancelling and/or joining it if necessary */
+    if( stream->callbackMode )
+    {
+        PaError threadRes;
+        stream->callbackAbort = abort;
+        if( abort )
+        {
+            PA_DEBUG(( "Aborting callback\n" ));
+        }
+        else
+        {
+            PA_DEBUG(( "Stopping callback\n" ));
+        }
+        PA_ENSURE_( PaUnixThread_Terminate( &stream->thread, !abort, &threadRes ) );
+        if( threadRes != paNoError )
+        {
+            PA_DEBUG(( "Callback thread returned: %d\n", threadRes ));
+        }
+    }
+    else
+    {
+        PA_ENSURE_( PaAsiHpi_StopStream( stream, abort ) );
+    }
+
+    stream->state = paAsiHpiStoppedState;
+
+error:
+    return result;
+}
+
+
+/** Stop PortAudio stream.
+ This blocks until the output buffers are drained.
+
+ @param s Pointer to PortAudio stream
+
+ @return PortAudio error code
+*/
+static PaError StopStream( PaStream *s )
+{
+    return PaAsiHpi_ExplicitStop( (PaAsiHpiStream *) s, 0 );
+}
+
+
+/** Abort PortAudio stream.
+ This discards any existing data in output buffers and stops the stream immediately.
+
+ @param s Pointer to PortAudio stream
+
+ @return PortAudio error code
+*/
+static PaError AbortStream( PaStream *s )
+{
+    return PaAsiHpi_ExplicitStop( (PaAsiHpiStream * ) s, 1 );
+}
+
+
+/** Determine whether the stream is stopped.
+ A stream is considered to be stopped prior to a successful call to StartStream and after
+ a successful call to StopStream or AbortStream. If a stream callback returns a value other
+ than paContinue the stream is NOT considered to be stopped (it is in CallbackFinished state).
+
+ @param s Pointer to PortAudio stream
+
+ @return Returns one (1) when the stream is stopped, zero (0) when the stream is running, or
+         a PaErrorCode (which are always negative) if PortAudio is not initialized or an
+         error is encountered.
+*/
+static PaError IsStreamStopped( PaStream *s )
+{
+    PaAsiHpiStream *stream = (PaAsiHpiStream*)s;
+
+    assert( stream );
+    return stream->state == paAsiHpiStoppedState ? 1 : 0;
+}
+
+
+/** Determine whether the stream is active.
+ A stream is active after a successful call to StartStream(), until it becomes inactive either
+ as a result of a call to StopStream() or AbortStream(), or as a result of a return value
+ other than paContinue from the stream callback. In the latter case, the stream is considered
+ inactive after the last buffer has finished playing.
+
+ @param s Pointer to PortAudio stream
+
+ @return Returns one (1) when the stream is active (i.e. playing or recording audio),
+         zero (0) when not playing, or a PaErrorCode (which are always negative)
+         if PortAudio is not initialized or an error is encountered.
+*/
+static PaError IsStreamActive( PaStream *s )
+{
+    PaAsiHpiStream *stream = (PaAsiHpiStream*)s;
+
+    assert( stream );
+    return stream->state == paAsiHpiActiveState ? 1 : 0;
+}
+
+
+/** Returns current stream time.
+ This corresponds to the system clock. The clock should run continuously while the stream
+ is open, i.e. between calls to OpenStream() and CloseStream(), therefore a frame counter
+ is not good enough.
+
+ @param s Pointer to PortAudio stream
+
+ @return Stream time, in seconds
+ */
+static PaTime GetStreamTime( PaStream *s )
+{
+    return PaUtil_GetTime();
+}
+
+
+/** Returns CPU load.
+
+ @param s Pointer to PortAudio stream
+
+ @return CPU load (0.0 if blocking interface is used)
+ */
+static double GetStreamCpuLoad( PaStream *s )
+{
+    PaAsiHpiStream *stream = (PaAsiHpiStream*)s;
+
+    return stream->callbackMode ? PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer ) : 0.0;
+}
+
+/* --------------------------- Callback Interface --------------------------- */
+
+/** Exit routine which is called when callback thread quits.
+ This takes care of stopping the HPI streams (either waiting for output to finish, or
+ abruptly). It also calls the user-supplied StreamFinished callback, and sets the
+ stream state to CallbackFinished if it was reached via a non-paContinue return from
+ the user callback function.
+
+ @param userData A pointer to an open stream previously created with Pa_OpenStream
+ */
+static void PaAsiHpi_OnThreadExit( void *userData )
+{
+    PaAsiHpiStream *stream = (PaAsiHpiStream *) userData;
+
+    assert( stream );
+
+    PaUtil_ResetCpuLoadMeasurer( &stream->cpuLoadMeasurer );
+
+    PA_DEBUG(( "%s: Stopping HPI streams\n", __FUNCTION__ ));
+    PaAsiHpi_StopStream( stream, stream->callbackAbort );
+    PA_DEBUG(( "%s: Stoppage\n", __FUNCTION__ ));
+
+    /* Eventually notify user all buffers have played */
+    if( stream->baseStreamRep.streamFinishedCallback )
+    {
+        stream->baseStreamRep.streamFinishedCallback( stream->baseStreamRep.userData );
+    }
+
+    /* Unfortunately both explicit calls to Stop/AbortStream (leading to Stopped state)
+     and implicit stops via paComplete/paAbort (leading to CallbackFinished state)
+     end up here - need another flag to remind us which is the case */
+    if( stream->callbackFinished )
+        stream->state = paAsiHpiCallbackFinishedState;
+}
+
+
+/** Wait until there is enough frames to fill a host buffer.
+ The routine attempts to sleep until at least a full host buffer can be retrieved from the
+ input HPI stream and passed to the output HPI stream. It will first sleep until enough
+ output space is available, as this is usually easily achievable. If it is an output-only
+ stream, it will also sleep if the hardware buffer is too full, thereby throttling the
+ filling of the output buffer and reducing output latency. The routine then blocks until
+ enough input samples are available, unless this will cause an output underflow. In the
+ process, input overflows and output underflows are indicated.
+
+ @param stream Pointer to stream struct
+
+ @param framesAvail Returns the number of available frames
+
+ @param cbFlags Overflows and underflows indicated in here
+
+ @return PortAudio error code (only paUnanticipatedHostError expected)
+ */
+static PaError PaAsiHpi_WaitForFrames( PaAsiHpiStream *stream, unsigned long *framesAvail,
+                                       PaStreamCallbackFlags *cbFlags )
+{
+    PaError result = paNoError;
+    double sampleRate;
+    unsigned long framesTarget;
+    HW32 outputData = 0, outputSpace = 0, inputData = 0, framesLeft = 0;
+
+    assert( stream );
+    assert( stream->input || stream->output );
+
+    sampleRate = stream->baseStreamRep.streamInfo.sampleRate;
+    /* We have to come up with this much frames on both input and output */
+    framesTarget = stream->bufferProcessor.framesPerHostBuffer;
+    assert( framesTarget > 0 );
+
+    while( 1 )
+    {
+        PaAsiHpiStreamInfo info;
+        /* Check output first, as this takes priority in the default full-duplex mode */
+        if( stream->output )
+        {
+            PA_ENSURE_( PaAsiHpi_GetStreamInfo( stream->output, &info ) );
+            /* Wait until enough space is available in output buffer to receive a full block */
+            if( info.availableFrames < framesTarget )
+            {
+                framesLeft = framesTarget - info.availableFrames;
+                Pa_Sleep( (long)ceil( 1000 * framesLeft / sampleRate ) );
+                continue;
+            }
+            /* Wait until the data in hardware buffer has dropped to a sensible level.
+             Without this, the hardware buffer quickly fills up in the absence of an input
+             stream to regulate its data rate (if data generation is fast). This leads to
+             large latencies, as the AudioScience hardware buffers are humongous.
+             This is similar to the default "Hardware Buffering=off" option in the
+             AudioScience WAV driver. */
+            if( !stream->input && (stream->output->outputBufferCap > 0) &&
+                    ( info.totalBufferedData > stream->output->outputBufferCap / stream->output->bytesPerFrame ) )
+            {
+                framesLeft = info.totalBufferedData - stream->output->outputBufferCap / stream->output->bytesPerFrame;
+                Pa_Sleep( (long)ceil( 1000 * framesLeft / sampleRate ) );
+                continue;
+            }
+            outputData = info.totalBufferedData;
+            outputSpace = info.availableFrames;
+            /* Report output underflow to callback */
+            if( info.underflow )
+            {
+                *cbFlags |= paOutputUnderflow;
+            }
+        }
+
+        /* Now check input side */
+        if( stream->input )
+        {
+            PA_ENSURE_( PaAsiHpi_GetStreamInfo( stream->input, &info ) );
+            /* If a full block of samples hasn't been recorded yet, wait for it if possible */
+            if( info.availableFrames < framesTarget )
+            {
+                framesLeft = framesTarget - info.availableFrames;
+                /* As long as output is not disrupted in the process, wait for a full
+                block of input samples */
+                if( !stream->output || (outputData > framesLeft) )
+                {
+                    Pa_Sleep( (long)ceil( 1000 * framesLeft / sampleRate ) );
+                    continue;
+                }
+            }
+            inputData = info.availableFrames;
+            /** @todo The paInputOverflow flag should be set in the callback containing the
+             first input sample following the overflow. That means the block currently sitting
+             at the fore-front of recording, i.e. typically the one containing the newest (last)
+             sample in the HPI buffer system. This is most likely not the same as the current
+             block of data being passed to the callback. The current overflow should ideally
+             be noted in an overflow list of sorts, with an indication of when it should be
+             reported. The trouble starts if there are several separate overflow incidents,
+             given a big input buffer. Oh well, something to try out later... */
+            if( info.overflow )
+            {
+                *cbFlags |= paInputOverflow;
+            }
+        }
+        break;
+    }
+    /* Full-duplex stream */
+    if( stream->input && stream->output )
+    {
+        if( outputSpace >= framesTarget )
+            *framesAvail = outputSpace;
+        /* If input didn't make the target, keep the output count instead (input underflow) */
+        if( (inputData >= framesTarget) && (inputData < outputSpace) )
+            *framesAvail = inputData;
+    }
+    else
+    {
+        *framesAvail = stream->input ? inputData : outputSpace;
+    }
+
+error:
+    return result;
+}
+
+
+/** Obtain recording, current and playback timestamps of stream.
+ The current time is determined by the system clock. This "now" timestamp occurs at the
+ forefront of recording (and playback in the full-duplex case), which happens later than the
+ input timestamp by an amount equal to the total number of recorded frames in the input buffer.
+ The output timestamp indicates when the next generated sample will actually be played. This
+ happens after all the samples currently in the output buffer are played. The output timestamp
+ therefore follows the current timestamp by an amount equal to the number of frames yet to be
+ played back in the output buffer.
+
+ If the current timestamp is the present, the input timestamp is in the past and the output
+ timestamp is in the future.
+
+ @param stream Pointer to stream struct
+
+ @param timeInfo Pointer to timeInfo struct that will contain timestamps
+ */
+static void PaAsiHpi_CalculateTimeInfo( PaAsiHpiStream *stream, PaStreamCallbackTimeInfo *timeInfo )
+{
+    PaAsiHpiStreamInfo streamInfo;
+    double sampleRate;
+
+    assert( stream );
+    assert( timeInfo );
+    sampleRate = stream->baseStreamRep.streamInfo.sampleRate;
+
+    /* The current time ("now") is at the forefront of both recording and playback */
+    timeInfo->currentTime = GetStreamTime( (PaStream *)stream );
+    /* The last sample in the input buffer was recorded just now, so the first sample
+     happened (number of recorded samples)/sampleRate ago */
+    timeInfo->inputBufferAdcTime = timeInfo->currentTime;
+    if( stream->input )
+    {
+        PaAsiHpi_GetStreamInfo( stream->input, &streamInfo );
+        timeInfo->inputBufferAdcTime -= streamInfo.totalBufferedData / sampleRate;
+    }
+    /* The first of the outgoing samples will be played after all the samples in the output
+     buffer is done */
+    timeInfo->outputBufferDacTime = timeInfo->currentTime;
+    if( stream->output )
+    {
+        PaAsiHpi_GetStreamInfo( stream->output, &streamInfo );
+        timeInfo->outputBufferDacTime += streamInfo.totalBufferedData / sampleRate;
+    }
+}
+
+
+/** Read from HPI input stream and register buffers.
+ This reads data from the HPI input stream (if it exists) and registers the temp stream
+ buffers of both input and output streams with the buffer processor. In the process it also
+ handles input underflows in the full-duplex case.
+
+ @param stream Pointer to stream struct
+
+ @param numFrames On entrance the number of available frames, on exit the number of
+                  received frames
+
+ @param cbFlags Indicates overflows and underflows
+
+ @return PortAudio error code
+ */
+static PaError PaAsiHpi_BeginProcessing( PaAsiHpiStream *stream, unsigned long *numFrames,
+        PaStreamCallbackFlags *cbFlags )
+{
+    PaError result = paNoError;
+
+    assert( stream );
+    if( *numFrames > stream->maxFramesPerHostBuffer )
+        *numFrames = stream->maxFramesPerHostBuffer;
+
+    if( stream->input )
+    {
+        PaAsiHpiStreamInfo info;
+        HPI_DATA data;
+        HW32 framesToGet = *numFrames;
+
+        /* Check for overflows and underflows yet again */
+        PA_ENSURE_( PaAsiHpi_GetStreamInfo( stream->input, &info ) );
+        if( info.overflow )
+        {
+            *cbFlags |= paInputOverflow;
+        }
+        /* Input underflow if less than expected number of samples pitch up */
+        if( framesToGet > info.availableFrames )
+        {
+            PaUtilZeroer *zeroer;
+            PaSampleFormat inputFormat;
+
+            /* Never call an input-only stream with InputUnderflow set */
+            if( stream->output )
+                *cbFlags |= paInputUnderflow;
+            framesToGet = info.availableFrames;
+            /* Fill temp buffer with silence (to make up for missing input samples) */
+            inputFormat = PaAsiHpi_HpiToPaFormat( stream->input->hpiFormat.wFormat );
+            zeroer = PaUtil_SelectZeroer( inputFormat );
+            zeroer(stream->input->tempBuffer, 1,
+                   stream->input->tempBufferSize / Pa_GetSampleSize(inputFormat) );
+        }
+
+        /* Setup HPI data structure around temp buffer */
+        HPI_DataCreate( &data, &stream->input->hpiFormat, stream->input->tempBuffer,
+                        framesToGet * stream->input->bytesPerFrame );
+        /* Read block of data into temp buffer */
+        PA_ASIHPI_UNLESS_( HPI_InStreamRead( stream->input->hpiDevice->subSys,
+                                             stream->input->hpiStream, &data ),
+                           paUnanticipatedHostError );
+        /* Register temp buffer with buffer processor (always FULL buffer) */
+        PaUtil_SetInputFrameCount( &stream->bufferProcessor, *numFrames );
+        /* HPI interface only allows interleaved channels */
+        PaUtil_SetInterleavedInputChannels( &stream->bufferProcessor,
+                                            0, stream->input->tempBuffer,
+                                            stream->input->hpiFormat.wChannels );
+    }
+    if( stream->output )
+    {
+        /* Register temp buffer with buffer processor */
+        PaUtil_SetOutputFrameCount( &stream->bufferProcessor, *numFrames );
+        /* HPI interface only allows interleaved channels */
+        PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor,
+                                             0, stream->output->tempBuffer,
+                                             stream->output->hpiFormat.wChannels );
+    }
+
+error:
+    return result;
+}
+
+
+/** Flush output buffers to HPI output stream.
+ This completes the processing cycle by writing the temp buffer to the HPI interface.
+ Additional output underflows are caught before data is written to the stream, as this
+ action typically remedies the underflow and hides it in the process.
+
+ @param stream Pointer to stream struct
+
+ @param numFrames The number of frames to write to the output stream
+
+ @param cbFlags Indicates overflows and underflows
+ */
+static PaError PaAsiHpi_EndProcessing( PaAsiHpiStream *stream, unsigned long numFrames,
+                                       PaStreamCallbackFlags *cbFlags )
+{
+    PaError result = paNoError;
+
+    assert( stream );
+
+    if( stream->output )
+    {
+        PaAsiHpiStreamInfo info;
+        HPI_DATA data;
+
+        /* Check for underflows after the (potentially time-consuming) callback */
+        PA_ENSURE_( PaAsiHpi_GetStreamInfo( stream->output, &info ) );
+        if( info.underflow )
+        {
+            *cbFlags |= paOutputUnderflow;
+        }
+
+        /* Setup HPI data structure around temp buffer */
+        HPI_DataCreate( &data, &stream->output->hpiFormat, stream->output->tempBuffer,
+                        numFrames * stream->output->bytesPerFrame );
+        /* Write temp buffer to HPI stream */
+        PA_ASIHPI_UNLESS_( HPI_OutStreamWrite( stream->output->hpiDevice->subSys,
+                                               stream->output->hpiStream, &data ),
+                           paUnanticipatedHostError );
+    }
+
+error:
+    return result;
+}
+
+
+/** Main callback engine.
+ This function runs in a separate thread and does all the work of fetching audio data from
+ the AudioScience card via the HPI interface, feeding it to the user callback via the buffer
+ processor, and delivering the resulting output data back to the card via HPI calls.
+ It is started and terminated when the PortAudio stream is started and stopped, and starts
+ the HPI streams on startup.
+
+ @param userData A pointer to an open stream previously created with Pa_OpenStream.
+*/
+static void *CallbackThreadFunc( void *userData )
+{
+    PaError result = paNoError;
+    PaAsiHpiStream *stream = (PaAsiHpiStream *) userData;
+    int callbackResult = paContinue;
+
+    assert( stream );
+
+    /* Cleanup routine stops streams on thread exit */
+    pthread_cleanup_push( &PaAsiHpi_OnThreadExit, stream );
+
+    /* Start HPI streams and notify parent when we're done */
+    PA_ENSURE_( PaUnixThread_PrepareNotify( &stream->thread ) );
+    /* Buffer will be primed with silence */
+    PA_ENSURE_( PaAsiHpi_StartStream( stream, 0 ) );
+    PA_ENSURE_( PaUnixThread_NotifyParent( &stream->thread ) );
+
+    /* MAIN LOOP */
+    while( 1 )
+    {
+        PaStreamCallbackFlags cbFlags = 0;
+        unsigned long framesAvail, framesGot;
+
+        pthread_testcancel();
+
+        /** @concern StreamStop if the main thread has requested a stop and the stream has not
+        * been effectively stopped we signal this condition by modifying callbackResult
+        * (we'll want to flush buffered output). */
+        if( PaUnixThread_StopRequested( &stream->thread ) && (callbackResult == paContinue) )
+        {
+            PA_DEBUG(( "Setting callbackResult to paComplete\n" ));
+            callbackResult = paComplete;
+        }
+
+        /* Start winding down thread if requested */
+        if( callbackResult != paContinue )
+        {
+            stream->callbackAbort = (callbackResult == paAbort);
+            if( stream->callbackAbort ||
+                    /** @concern BlockAdaption: Go on if adaption buffers are empty */
+                    PaUtil_IsBufferProcessorOutputEmpty( &stream->bufferProcessor ) )
+            {
+                goto end;
+            }
+            PA_DEBUG(( "%s: Flushing buffer processor\n", __FUNCTION__ ));
+            /* There is still buffered output that needs to be processed */
+        }
+
+        /* SLEEP */
+        /* Wait for data (or buffer space) to become available. This basically sleeps and
+        polls the HPI interface until a full block of frames can be moved. */
+        PA_ENSURE_( PaAsiHpi_WaitForFrames( stream, &framesAvail, &cbFlags ) );
+
+        /* Consume buffer space. Once we have a number of frames available for consumption we
+        must retrieve the data from the HPI interface and pass it to the PA buffer processor.
+        We should be prepared to process several chunks successively. */
+        while( framesAvail > 0 )
+        {
+            PaStreamCallbackTimeInfo timeInfo = {0, 0, 0};
+
+            pthread_testcancel();
+
+            framesGot = framesAvail;
+            if( stream->bufferProcessor.hostBufferSizeMode == paUtilFixedHostBufferSize )
+            {
+                /* We've committed to a fixed host buffer size, stick to that */
+                framesGot = framesGot >= stream->maxFramesPerHostBuffer ? stream->maxFramesPerHostBuffer : 0;
+            }
+            else
+            {
+                /* We've committed to an upper bound on the size of host buffers */
+                assert( stream->bufferProcessor.hostBufferSizeMode == paUtilBoundedHostBufferSize );
+                framesGot = PA_MIN( framesGot, stream->maxFramesPerHostBuffer );
+            }
+
+            /* Obtain buffer timestamps */
+            PaAsiHpi_CalculateTimeInfo( stream, &timeInfo );
+            PaUtil_BeginBufferProcessing( &stream->bufferProcessor, &timeInfo, cbFlags );
+            /* CPU load measurement should include processing activivity external to the stream callback */
+            PaUtil_BeginCpuLoadMeasurement( &stream->cpuLoadMeasurer );
+            if( framesGot > 0 )
+            {
+                /* READ FROM HPI INPUT STREAM */
+                PA_ENSURE_( PaAsiHpi_BeginProcessing( stream, &framesGot, &cbFlags ) );
+                /* Input overflow in a full-duplex stream makes for interesting times */
+                if( stream->input && stream->output && (cbFlags & paInputOverflow) )
+                {
+                    /* Special full-duplex paNeverDropInput mode */
+                    if( stream->neverDropInput )
+                    {
+                        PaUtil_SetNoOutput( &stream->bufferProcessor );
+                        cbFlags |= paOutputOverflow;
+                    }
+                }
+                /* CALL USER CALLBACK WITH INPUT DATA, AND OBTAIN OUTPUT DATA */
+                PaUtil_EndBufferProcessing( &stream->bufferProcessor, &callbackResult );
+                /* Clear overflow and underflow information (but PaAsiHpi_EndProcessing might
+                still show up output underflow that will carry over to next round) */
+                cbFlags = 0;
+                /*  WRITE TO HPI OUTPUT STREAM */
+                PA_ENSURE_( PaAsiHpi_EndProcessing( stream, framesGot, &cbFlags ) );
+                /* Advance frame counter */
+                framesAvail -= framesGot;
+            }
+            PaUtil_EndCpuLoadMeasurement( &stream->cpuLoadMeasurer, framesGot );
+
+            if( framesGot == 0 )
+            {
+                /* Go back to polling for more frames */
+                break;
+
+            }
+            if( callbackResult != paContinue )
+                break;
+        }
+    }
+
+    /* This code is unreachable, but important to include regardless because it
+     * is possibly a macro with a closing brace to match the opening brace in
+     * pthread_cleanup_push() above.  The documentation states that they must
+     * always occur in pairs. */
+    pthread_cleanup_pop( 1 );
+
+end:
+    /* Indicates normal exit of callback, as opposed to the thread getting killed explicitly */
+    stream->callbackFinished = 1;
+    PA_DEBUG(( "%s: Thread %d exiting (callbackResult = %d)\n ",
+               __FUNCTION__, pthread_self(), callbackResult ));
+    /* Exit from thread and report any PortAudio error in the process */
+    PaUnixThreading_EXIT( result );
+error:
+    goto end;
+}
+
+/* --------------------------- Blocking Interface --------------------------- */
+
+/* As separate stream interfaces are used for blocking and callback streams, the following
+ functions can be guaranteed to only be called for blocking streams. */
+
+/** Read data from input stream.
+ This reads the indicated number of frames into the supplied buffer from an input stream,
+ and blocks until this is done.
+
+ @param s Pointer to PortAudio stream
+
+ @param buffer Pointer to buffer that will receive interleaved data (or an array of pointers
+               to a buffer for each non-interleaved channel)
+
+ @param frames Number of frames to read from stream
+
+ @return PortAudio error code (also indicates overflow via paInputOverflowed)
+ */
+static PaError ReadStream( PaStream *s,
+                           void *buffer,
+                           unsigned long frames )
+{
+    PaError result = paNoError;
+    PaAsiHpiStream *stream = (PaAsiHpiStream*)s;
+    PaAsiHpiStreamInfo info;
+    void *userBuffer;
+
+    assert( stream );
+    PA_UNLESS_( stream->input, paCanNotReadFromAnOutputOnlyStream );
+
+    /* Check for input overflow since previous call to ReadStream */
+    PA_ENSURE_( PaAsiHpi_GetStreamInfo( stream->input, &info ) );
+    if( info.overflow )
+    {
+        result = paInputOverflowed;
+    }
+
+    /* NB Make copy of user buffer pointers, since they are advanced by buffer processor */
+    if( stream->bufferProcessor.userInputIsInterleaved )
+    {
+        userBuffer = buffer;
+    }
+    else
+    {
+        /* Copy channels into local array */
+        userBuffer = stream->blockingUserBufferCopy;
+        memcpy( userBuffer, buffer, sizeof (void *) * stream->input->hpiFormat.wChannels );
+    }
+
+    while( frames > 0 )
+    {
+        unsigned long framesGot, framesAvail;
+        PaStreamCallbackFlags cbFlags = 0;
+
+        PA_ENSURE_( PaAsiHpi_WaitForFrames( stream, &framesAvail, &cbFlags ) );
+        framesGot = PA_MIN( framesAvail, frames );
+        PA_ENSURE_( PaAsiHpi_BeginProcessing( stream, &framesGot, &cbFlags ) );
+
+        if( framesGot > 0 )
+        {
+            framesGot = PaUtil_CopyInput( &stream->bufferProcessor, &userBuffer, framesGot );
+            PA_ENSURE_( PaAsiHpi_EndProcessing( stream, framesGot, &cbFlags ) );
+            /* Advance frame counter */
+            frames -= framesGot;
+        }
+    }
+
+error:
+    return result;
+}
+
+
+/** Write data to output stream.
+ This writes the indicated number of frames from the supplied buffer to an output stream,
+ and blocks until this is done.
+
+ @param s Pointer to PortAudio stream
+
+ @param buffer Pointer to buffer that provides interleaved data (or an array of pointers
+               to a buffer for each non-interleaved channel)
+
+ @param frames Number of frames to write to stream
+
+ @return PortAudio error code (also indicates underflow via paOutputUnderflowed)
+ */
+static PaError WriteStream( PaStream *s,
+                            const void *buffer,
+                            unsigned long frames )
+{
+    PaError result = paNoError;
+    PaAsiHpiStream *stream = (PaAsiHpiStream*)s;
+    PaAsiHpiStreamInfo info;
+    const void *userBuffer;
+
+    assert( stream );
+    PA_UNLESS_( stream->output, paCanNotWriteToAnInputOnlyStream );
+
+    /* Check for output underflow since previous call to WriteStream */
+    PA_ENSURE_( PaAsiHpi_GetStreamInfo( stream->output, &info ) );
+    if( info.underflow )
+    {
+        result = paOutputUnderflowed;
+    }
+
+    /* NB Make copy of user buffer pointers, since they are advanced by buffer processor */
+    if( stream->bufferProcessor.userOutputIsInterleaved )
+    {
+        userBuffer = buffer;
+    }
+    else
+    {
+        /* Copy channels into local array */
+        userBuffer = stream->blockingUserBufferCopy;
+        memcpy( (void *)userBuffer, buffer, sizeof (void *) * stream->output->hpiFormat.wChannels );
+    }
+
+    while( frames > 0 )
+    {
+        unsigned long framesGot, framesAvail;
+        PaStreamCallbackFlags cbFlags = 0;
+
+        PA_ENSURE_( PaAsiHpi_WaitForFrames( stream, &framesAvail, &cbFlags ) );
+        framesGot = PA_MIN( framesAvail, frames );
+        PA_ENSURE_( PaAsiHpi_BeginProcessing( stream, &framesGot, &cbFlags ) );
+
+        if( framesGot > 0 )
+        {
+            framesGot = PaUtil_CopyOutput( &stream->bufferProcessor, &userBuffer, framesGot );
+            PA_ENSURE_( PaAsiHpi_EndProcessing( stream, framesGot, &cbFlags ) );
+            /* Advance frame counter */
+            frames -= framesGot;
+        }
+    }
+
+error:
+    return result;
+}
+
+
+/** Number of frames that can be read from input stream without blocking.
+
+ @param s Pointer to PortAudio stream
+
+ @return Number of frames, or PortAudio error code
+ */
+static signed long GetStreamReadAvailable( PaStream *s )
+{
+    PaError result = paNoError;
+    PaAsiHpiStream *stream = (PaAsiHpiStream*)s;
+    PaAsiHpiStreamInfo info;
+
+    assert( stream );
+    PA_UNLESS_( stream->input, paCanNotReadFromAnOutputOnlyStream );
+
+    PA_ENSURE_( PaAsiHpi_GetStreamInfo( stream->input, &info ) );
+    /* Round down to the nearest host buffer multiple */
+    result = (info.availableFrames / stream->maxFramesPerHostBuffer) * stream->maxFramesPerHostBuffer;
+    if( info.overflow )
+    {
+        result = paInputOverflowed;
+    }
+
+error:
+    return result;
+}
+
+
+/** Number of frames that can be written to output stream without blocking.
+
+ @param s Pointer to PortAudio stream
+
+ @return Number of frames, or PortAudio error code
+ */
+static signed long GetStreamWriteAvailable( PaStream *s )
+{
+    PaError result = paNoError;
+    PaAsiHpiStream *stream = (PaAsiHpiStream*)s;
+    PaAsiHpiStreamInfo info;
+
+    assert( stream );
+    PA_UNLESS_( stream->output, paCanNotWriteToAnInputOnlyStream );
+
+    PA_ENSURE_( PaAsiHpi_GetStreamInfo( stream->output, &info ) );
+    /* Round down to the nearest host buffer multiple */
+    result = (info.availableFrames / stream->maxFramesPerHostBuffer) * stream->maxFramesPerHostBuffer;
+    if( info.underflow )
+    {
+        result = paOutputUnderflowed;
+    }
+
+error:
+    return result;
+}



Julius-cvs メーリングリストの案内
アーカイブの一覧に戻る