PortAudio  2.0
Data Structures | Macros | Typedefs | Enumerations | Functions
pa_linux_asihpi.c File Reference

Host API implementation supporting AudioScience cards via the Linux HPI interface. More...

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <assert.h>
#include <math.h>
#include <asihpi/hpi.h>
#include "portaudio.h"
#include "pa_util.h"
#include "pa_unix_util.h"
#include "pa_allocation.h"
#include "pa_hostapi.h"
#include "pa_stream.h"
#include "pa_cpuload.h"
#include "pa_process.h"
#include "pa_converters.h"
#include "pa_debugprint.h"

Data Structures

struct  PaAsiHpiHostApiRepresentation
 
struct  PaAsiHpiDeviceInfo
 
struct  PaAsiHpiStreamComponent
 
struct  PaAsiHpiStream
 
struct  PaAsiHpiStreamInfo
 

Macros

#define PA_ENSURE_(expr)
 
#define PA_UNLESS_(expr, paError)
 
#define PA_ASIHPI_UNLESS_(expr, paError)
 
#define PA_ASIHPI_REPORT_ERROR_(hpiErrorCode)
 
#define PA_ASIHPI_AVAILABLE_FORMATS_   (paFloat32 | paInt32 | paInt24 | paInt16 | paUInt8)
 
#define PA_ASIHPI_USE_BBM_   1
 
#define PA_ASIHPI_MIN_FRAMES_   1152
 
#define PA_ASIHPI_MIN_POLLING_INTERVAL_   10
 

Typedefs

typedef struct
PaAsiHpiHostApiRepresentation 
PaAsiHpiHostApiRepresentation
 
typedef struct PaAsiHpiDeviceInfo PaAsiHpiDeviceInfo
 
typedef enum PaAsiHpiStreamState PaAsiHpiStreamState
 
typedef struct
PaAsiHpiStreamComponent 
PaAsiHpiStreamComponent
 
typedef struct PaAsiHpiStream PaAsiHpiStream
 
typedef struct PaAsiHpiStreamInfo PaAsiHpiStreamInfo
 

Enumerations

enum  PaAsiHpiStreamState { paAsiHpiStoppedState =0, paAsiHpiActiveState =1, paAsiHpiCallbackFinishedState =2 }
 

Functions

PaError PaAsiHpi_Initialize (PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index)
 

Detailed Description

Host API implementation supporting AudioScience cards via the Linux HPI interface.

Overview

This is a PortAudio implementation for the AudioScience HPI Audio API on the Linux platform. AudioScience makes a range of audio adapters customised for the broadcasting industry, with support for both Windows and Linux. More information on their products can be found on their website:

http://www.audioscience.com

Documentation for the HPI API can be found at:

http://www.audioscience.com/internet/download/sdk/hpi_usermanual_html/html/index.html

The Linux HPI driver itself (a kernel module + library) can be downloaded from:

http://www.audioscience.com/internet/download/linux_drivers.htm

Implementation strategy

Note* Ideally, AudioScience cards should be handled by the PortAudio ALSA implementation on Linux, as ALSA is the preferred Linux soundcard API. The existence of this host API implementation might therefore seem a bit flawed. Unfortunately, at the time of the creation of this implementation (June 2006), the PA ALSA implementation could not make use of the existing AudioScience ALSA driver. PA ALSA uses the "memory-mapped" (mmap) ALSA access mode to interact with the ALSA library, while the AudioScience ALSA driver only supports the "read-write" access mode. The appropriate solution to this problem is to add "read-write" support to PortAudio ALSA, thereby extending the range of soundcards it supports (AudioScience cards are not the only ones with this problem). Given the author's limited knowledge of ALSA and the simplicity of the HPI API, the second-best solution was born...

The following mapping between HPI and PA was followed: HPI subsystem => PortAudio host API HPI adapter => nothing specific HPI stream => PortAudio device

Each HPI stream is either input or output (not both), and can support different channel counts, sampling rates and sample formats. It is therefore a more natural fit to a PA device. A PA stream can therefore combine two HPI streams (one input and one output) into a "full-duplex" stream. These HPI streams can even be on different physical adapters. The two streams ought to be sample-synchronised when they reside on the same adapter, as most AudioScience adapters derive their ADC and DAC clocks from one master clock. When combining two adapters into one full-duplex stream, however, the use of a word clock connection between the adapters is strongly recommended.

The HPI interface is inherently blocking, making use of read and write calls to transfer data between user buffers and driver buffers. The callback interface therefore requires a helper thread ("callback engine") which periodically transfers data (one thread per PA stream, in fact). The current implementation explicitly sleeps via Pa_Sleep() until enough samples can be transferred (select() or poll() would be better, but currently seems impossible...). The thread implementation makes use of the Unix thread helper functions and some pthread calls here and there. If a unified PA thread exists, this host API implementation might also compile on Windows, as this is the only real Linux-specific part of the code.

There is no inherent fixed buffer size in the HPI interface, as in some other host APIs. The PortAudio implementation contains a buffer that is allocated during OpenStream and used to transfer data between the callback and the HPI driver buffer. The size of this buffer is quite flexible and is derived from latency suggestions and matched to the requested callback buffer size as far as possible. It can become quite huge, as the AudioScience cards are typically geared towards higher-latency applications and contain large hardware buffers.

The HPI interface natively supports most common sample formats and sample rates (some conversion is done on the adapter itself).

Stream time is measured based on the number of processed frames, which is adjusted by the number of frames currently buffered by the HPI driver.

There is basic support for detecting overflow and underflow. The HPI interface does not explicitly indicate this, so thresholds on buffer levels are used in combination with stream state. Recovery from overflow and underflow is left to the PA client.

Blocking streams are also implemented. It makes use of the same polling routines that the callback interface uses, in order to prevent the allocation of variable-sized buffers during reading and writing. The framesPerBuffer parameter is therefore still relevant, and this can be increased in the blocking case to improve efficiency.

The implementation contains extensive reporting macros (slightly modified PA_ENSURE and PA_UNLESS versions) and a useful stream dump routine to provide debugging feedback.

Output buffer priming via the user callback (i.e. paPrimeOutputBuffersUsingStreamCallback and friends) is not implemented yet. All output is primed with silence.

Macro Definition Documentation

#define PA_ASIHPI_AVAILABLE_FORMATS_   (paFloat32 | paInt32 | paInt24 | paInt16 | paUInt8)

Sample formats available natively on AudioScience hardware

#define PA_ASIHPI_MIN_FRAMES_   1152

Minimum number of frames in HPI buffer (for either data or available space). If buffer contains less data/space, it indicates xrun or completion.

#define PA_ASIHPI_MIN_POLLING_INTERVAL_   10

Minimum polling interval in milliseconds, which determines minimum host buffer size

#define PA_ASIHPI_REPORT_ERROR_ (   hpiErrorCode)
Value:
do { \
char szError[256]; \
HPI_GetErrorText( hpiError, szError ); \
PA_DEBUG(( "HPI error %d occurred: %s\n", hpiError, szError )); \
/* PaUtil_SetLastHostErrorInfo should only be used in the main thread */ \
if( pthread_equal( pthread_self(), paUnixMainThread ) ) \
{ \
PaUtil_SetLastHostErrorInfo( paInDevelopment, (hpiErrorCode), szError ); \
} \
} while( 0 );
void PaUtil_SetLastHostErrorInfo(PaHostApiTypeId hostApiType, long errorCode, const char *errorText)
Definition: pa_front.c:142
pthread_t paUnixMainThread
Definition: pa_unix_util.c:244
Definition: portaudio.h:279
#define PA_DEBUG(x)
Definition: pa_debugprint.h:99

Report HPI error code and text

#define PA_ASIHPI_UNLESS_ (   expr,
  paError 
)
Value:
do { \
hpi_err_t hpiError = (expr); \
/* If HPI error occurred */ \
if( UNLIKELY( hpiError ) ) \
{ \
char szError[256]; \
HPI_GetErrorText( hpiError, szError ); \
PA_DEBUG(( "HPI error %d occurred: %s\n", hpiError, szError )); \
/* This message will always be displayed, even if debug info is disabled */ \
PA_DEBUG(( "Expression '" #expr "' failed in '" __FILE__ "', line: " PA_STRINGIZE( __LINE__ ) "\n" )); \
if( (paError) == paUnanticipatedHostError ) \
{ \
PA_DEBUG(( "Host error description: %s\n", szError )); \
/* PaUtil_SetLastHostErrorInfo should only be used in the main thread */ \
if( pthread_equal( pthread_self(), paUnixMainThread ) ) \
{ \
} \
} \
/* If paNoError is specified, continue as usual */ \
/* (useful if you only want to print out the debug messages above) */ \
if( (paError) < 0 ) \
{ \
result = (paError); \
goto error; \
} \
} \
} while( 0 );
void PaUtil_SetLastHostErrorInfo(PaHostApiTypeId hostApiType, long errorCode, const char *errorText)
Definition: pa_front.c:142
#define UNLIKELY(expr)
Definition: pa_unix_util.h:66
Definition: portaudio.h:128
pthread_t paUnixMainThread
Definition: pa_unix_util.c:244
#define PA_STRINGIZE(x)
Definition: pa_util.h:58
Definition: portaudio.h:279
#define PA_DEBUG(x)
Definition: pa_debugprint.h:99

Check return value of HPI function, and map it to PaError

Referenced by PaAsiHpi_Initialize().

#define PA_ASIHPI_USE_BBM_   1

Enable background bus mastering (BBM) for buffer transfers, if available (see HPI docs)

#define PA_ENSURE_ (   expr)
Value:
do { \
PaError paError = (expr); \
if( UNLIKELY( paError < paNoError ) ) \
{ \
PA_DEBUG(( "Expression '" #expr "' failed in '" __FILE__ "', line: " PA_STRINGIZE( __LINE__ ) "\n" )); \
result = paError; \
goto error; \
} \
} while (0);
#define UNLIKELY(expr)
Definition: pa_unix_util.h:66
Definition: portaudio.h:125
#define PA_STRINGIZE(x)
Definition: pa_util.h:58
int PaError
Definition: portaudio.h:122
#define PA_DEBUG(x)
Definition: pa_debugprint.h:99

Evaluate expression, and return on any PortAudio errors

Referenced by PaAsiHpi_Initialize().

#define PA_UNLESS_ (   expr,
  paError 
)
Value:
do { \
if( UNLIKELY( (expr) == 0 ) ) \
{ \
PA_DEBUG(( "Expression '" #expr "' failed in '" __FILE__ "', line: " PA_STRINGIZE( __LINE__ ) "\n" )); \
result = (paError); \
goto error; \
} \
} while( 0 );
#define UNLIKELY(expr)
Definition: pa_unix_util.h:66
#define PA_STRINGIZE(x)
Definition: pa_util.h:58
#define PA_DEBUG(x)
Definition: pa_debugprint.h:99

Assert expression, else return the provided PaError

Referenced by PaAsiHpi_Initialize().

Typedef Documentation

Device data

Host API global data

Stream data

Stream component data (associated with one direction, i.e. either input or output)

Stream state information, collected together for convenience

Stream state as defined by PortAudio. It seems that the host API implementation has to keep track of the PortAudio stream state. Please note that this is NOT the same as the state of the underlying HPI stream. By separating these two concepts, a lot of flexibility is gained. There is a rough match between the two, of course, but forcing a precise match is difficult. For example, HPI_STATE_DRAINED can occur during the Active state of PortAudio (due to underruns) and also during CallBackFinished in the case of an output stream. Similarly, HPI_STATE_STOPPED mostly coincides with the Stopped PortAudio state, by may also occur in the CallbackFinished state when recording is finished.

Here is a rough match-up:

PortAudio state => HPI state


Active => HPI_STATE_RECORDING, HPI_STATE_PLAYING, (HPI_STATE_DRAINED) Stopped => HPI_STATE_STOPPED CallbackFinished => HPI_STATE_STOPPED, HPI_STATE_DRAINED

Enumeration Type Documentation

Stream state as defined by PortAudio. It seems that the host API implementation has to keep track of the PortAudio stream state. Please note that this is NOT the same as the state of the underlying HPI stream. By separating these two concepts, a lot of flexibility is gained. There is a rough match between the two, of course, but forcing a precise match is difficult. For example, HPI_STATE_DRAINED can occur during the Active state of PortAudio (due to underruns) and also during CallBackFinished in the case of an output stream. Similarly, HPI_STATE_STOPPED mostly coincides with the Stopped PortAudio state, by may also occur in the CallbackFinished state when recording is finished.

Here is a rough match-up:

PortAudio state => HPI state


Active => HPI_STATE_RECORDING, HPI_STATE_PLAYING, (HPI_STATE_DRAINED) Stopped => HPI_STATE_STOPPED CallbackFinished => HPI_STATE_STOPPED, HPI_STATE_DRAINED

Enumerator
paAsiHpiStoppedState 
paAsiHpiActiveState 
paAsiHpiCallbackFinishedState 

Function Documentation

PaError PaAsiHpi_Initialize ( PaUtilHostApiRepresentation **  hostApi,
PaHostApiIndex  hostApiIndex 
)