PortAudio
2.0
|
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) |
Host API implementation supporting AudioScience cards via the Linux HPI interface.
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
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.
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 | ) |
Report HPI error code and text
#define PA_ASIHPI_UNLESS_ | ( | expr, | |
paError | |||
) |
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 | ) |
Evaluate expression, and return on any PortAudio errors
Referenced by PaAsiHpi_Initialize().
#define PA_UNLESS_ | ( | expr, | |
paError | |||
) |
Assert expression, else return the provided PaError
Referenced by PaAsiHpi_Initialize().
typedef struct PaAsiHpiDeviceInfo PaAsiHpiDeviceInfo |
Device data
typedef struct PaAsiHpiHostApiRepresentation PaAsiHpiHostApiRepresentation |
Host API global data
typedef struct PaAsiHpiStream PaAsiHpiStream |
Stream data
typedef struct PaAsiHpiStreamComponent PaAsiHpiStreamComponent |
Stream component data (associated with one direction, i.e. either input or output)
typedef struct PaAsiHpiStreamInfo PaAsiHpiStreamInfo |
Stream state information, collected together for convenience
typedef enum PaAsiHpiStreamState PaAsiHpiStreamState |
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
enum PaAsiHpiStreamState |
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 |
PaError PaAsiHpi_Initialize | ( | PaUtilHostApiRepresentation ** | hostApi, |
PaHostApiIndex | hostApiIndex | ||
) |
Initialize host API implementation. This is the only function exported beyond this file. It is called by PortAudio to initialize the host API. It stores API info, finds and registers all devices, and sets up callback and blocking interfaces.
hostApi | Pointer to host API struct |
hostApiIndex | Index of current (HPI) host API |
References PaAsiHpiHostApiRepresentation::allocations, PaAsiHpiHostApiRepresentation::baseHostApiRep, PaAsiHpiHostApiRepresentation::blockingStreamInterface, PaAsiHpiHostApiRepresentation::callbackStreamInterface, PaHostApiInfo::defaultInputDevice, PaHostApiInfo::defaultOutputDevice, PaHostApiInfo::deviceCount, PaAsiHpiHostApiRepresentation::hostApiIndex, PaHostApiInfo::name, PA_ASIHPI_UNLESS_, PA_DEBUG, PA_ENSURE_, PA_UNLESS_, paAudioScienceHPI, paInsufficientMemory, paNoDevice, paNoError, paUnanticipatedHostError, PaUnixThreading_Initialize(), PaUtil_AllocateMemory(), PaUtil_CreateAllocationGroup(), PaUtil_DummyGetCpuLoad(), PaUtil_DummyGetReadAvailable(), PaUtil_DummyGetWriteAvailable(), PaUtil_DummyRead(), PaUtil_DummyWrite(), PaUtil_FreeMemory(), PaUtil_InitializeStreamInterface(), PaHostApiInfo::structVersion, and PaHostApiInfo::type.