gearmulator

Emulation of classic VA synths of the late 90s/2000s that are based on Motorola 56300 family DSPs
Log | Files | Refs | Submodules | README | LICENSE

pa_linux_asihpi.c (117654B)


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