Enhancement Proposals Index, PortAudio Home Page
Updated: July 26, 2002
This proposal is essentially complete and has been implemented in v19-devel. Due to its dependence on a number of other unfinished proposals there is a small probability that this proposal may undergo further changes.
The API changes described in this proposal are independent of all other proposals. However, changes to the host error mechanism defined in 009 - Handling of Host API Specific Error Codes, and the addition of new API functions due to the 005 - Blocking Read/Write Interface proposal may effect the implementation of this proposal. Changes to the API defined by the 002 - Improve Device Formats Query Interface and the 003 - Improve Latency Specification Interface proposals will need to be multi-host-API capable.
At present (V18) PortAudio allows clients to link in support for only one host API, supporting multiple host APIs requires separate executables for each host API to be built. As the number of supported host APIs on each platform grows (we now support 3 host APIs under Windows for example) client applications need to be able to select between different host APIs at run-time. At least four platforms supported by PortAudio have more than one host audio API.
This proposal is based this email: http://techweb.rfa.org/pipermail/portaudio/2001-December/000308.html
It will be necessary to supply clients with a method of displaying a textual description of the host API for each PortAudio device.
Some PortAudio functions do not operate on PortAudioStreams, but rather they operate on or return the global state of the PortAudio library as a whole. If multiple host APIs were present, some of these functions would have different implementations, or would return different values depending on which host API they applied to. These functions include:
int Pa_GetMinNumBuffers( int framesPerBuffer, double sampleRate ); PaDeviceID Pa_GetDefaultInputDeviceID( void ); PaDeviceID Pa_GetDefaultOutputDeviceID( void );
Note that Pa_CountDevices() could also be interpreted as applying to a specific host API.
PortAudio currently defines a per-host-API extension mechanism via the inputDriverInfo and outputDriverInfo parameters to Pa_OpenStream(). For code to take advantage of host-API-specific extensions when multiple host APIs are present there needs to be a way to establish which (statically identified) host API is associated with each device. This is because host-API -specific extensions must only be used in combination with devices supplied by that host API.
PortAudio should present clients with all of the devices made available by each host API. This means that some physical devices may be accessible though multiple host APIs. Since it will not be possible to open a full duplex stream with input and output devices from different host APIs, some clients may want to enumerate the available host APIs and only display devices from one host API at a time.
Introducing a new PortAudioHostAPI abstraction could fulfil the above requirements. At a minimum, this abstraction would need to have the following attributes:
Additionally, the following features could be present:
This proposal consists of the 7 modifications to the PortAudio API listed below.
1. Define a new PaHostApiTypeId enumeration, with fixed values for each host API:
/* The PaHostApiTypeId enumeration contains unchanging unique identifiers for each supported host API. This type is used in the PaHostApiInfo structure. The values are guaranteed to be unique and to never change, thus allowing code to be written that conditionally uses host API specific extensions. New type ids will be allocated when support for a host API reaches "public alpha" status, prior to that developers should use the paInDevelopment type id. */ typedef enum { paInDevelopment=0, /* use while developing support for a new host API */ paDirectSound=1, paMME=2, paASIO=3, paSoundManager=4, paCoreAudio=5, paOSS=7, paALSA=8, paAL=9, paBeOS=10 }PaHostApiTypeId;
2. Define a method for enumerating host APIs:
/* Host API enumeration mechanism */ typedef int PaHostApiIndex; /**< The type used to enumerate to host APIs at runtime. Values of this type range from 0 to (Pa_CountHostApis()-1). @see Pa_CountHostApis */ /** Retrieve the number of available host APIs. Even if a host API is available it may have no devices available. @return The number of available host APIs. May return 0 if PortAudio is not initialized or an error has occured. @see PaHostApiIndex */ PaHostApiIndex Pa_CountHostApis( void ); /** Retrieve the index of the defualt hostAPI. The default host API will be the lowest common denominator host API on the current platform and is unlikely to provide the best performance. @return The default host API index. */ PaHostApiIndex Pa_GetDefaultHostApi( void ); /** Convert a static host API unique identifier, into a runtime host API index. @param type A unique host API identifier belonging to the PaHostApiTypeId enumeration. @return A valid PaHostApiIndex ranging from 0 to (Pa_CountHostApis()-1), or -1 if the host API specified by the type parameter is not available. @see PaHostApiTypeId */ PaHostApiIndex Pa_HostApiTypeIdToHostApiIndex( PaHostApiTypeId type );
3. Provide a method for retrieving information about a given host API:
/** A structure containing information about a particular host API. */ typedef struct { int structVersion; /** the well known unique identifier of this host API @see PaHostApiTypeId*/ PaHostApiTypeId type; /** a textual description of the host API for display on user interfaces */ const char *name; /** The number of devices belonging to this host API. This field may be used in conjunction with Pa_HostApiDeviceIndexToDeviceIndex() to enumerate all devices for this host API. @see Pa_HostApiDeviceIndexToDeviceIndex */ int deviceCount; /** The the default input device for this host API. The value will be a device index ranging from 0 to (Pa_CountDevices()-1), or paNoDevice if no default input device is available. */ PaDeviceIndex defaultInputDevice; /** The the default output device for this host API. The value will be a device index ranging from 0 to (Pa_CountDevices()-1), or paNoDevice if no default output device is available. */ PaDeviceIndex defaultOutputDevice; } PaHostApiInfo; /** Retrieve a pointer to a structure containing information about a specific host Api. @param hostApi A valid host API index ranging from 0 to (Pa_CountHostApis()-1) @return A pointer to an immutable PaHostApiInfo structure describing a specific host API. If the hostApi parameter is out of range or an error is encountered, the function returns NULL. The returned structure is owned by the PortAudio implementation and must not be manipulated or freed. The pointer is only guaranteed to be valid between calls to Pa_Initialize() and Pa_Terminate(). */ const PaHostApiInfo * Pa_GetHostApiInfo( PaHostApiIndex hostApi );
4. Add a new field to PaDeviceInfo to identify the host API type:
struct{ ... PaHostApiIndex hostApi; /* note this is a host API index, not a type id*/ ... }PaDeviceInfo;
This would enable the following two code fragments to be written.
/* obtain the user-readable name of a device's host API */ Pa_GetHostApiInfo( deviceInfo->hostApi )->name; /* implement special behavior for a specific host API */ if( Pa_GetHostApiInfo( deviceInfo->hostApi )->typeId == paWin32MME ){ InitialiseWmmeSpecificDeviceInfo(); }
5. Provide a facility for finding per-host API default devices. - This functionality is provided by the defaultInputDevice and defaultOutputDevice fields in the PaHostApiInfo structure.
6. Provide a facility for enumerating devices on a per-host-API basis. Note that this functionality is provided in addition to the current Pa_CountDevices() and Pa_GetDeviceInfo() functions:
/** Convert a host-API-specific device index to standard PortAudio device index. This function may be used in conjunction with the deviceCount field of PaHostApiInfo to enumerate all devices for the specified host API. @param hostApi A valid host API index ranging from 0 to (Pa_CountHostApis()-1) @param hostApiDeviceIndex A valid per-host device index in the range 0 to (Pa_GetHostApiInfo(hostApi)->deviceCount-1) @see PaHostApiInfo */ PaDeviceIndex Pa_HostApiDeviceIndexToDeviceIndex( PaHostApiIndex hostApi, int hostApiDeviceIndex );
7. Re-implement the following existing functions to use the default host API. This would be a backwards compatible change.
PaDeviceIndex Pa_GetDefaultInputDevice( void ); /* returns the default device id for the default host API */ PaDeviceIndex Pa_GetDefaultOutputDevice( void );
The main disadvantage of this proposal it that it may make the API seem more complex for new users.
There is concern that this proposal is too complex, and that the simpler solution of simply adding a hostAPI string to the device info structure of each device would be sufficient. It is true that the simple solution would allow clients to duplicate the functionality of this proposal, provided hostAPI strings were published and guaranteed not to change in the future. However, the bulk of the functionality included in this proposal will need to be implemented internally to facilitate multiple host API support anyway. This proposal is based on the assumption that it is better to expose such functionality in the PortAudio API rather than require clients to reimplement what is already present internally.
There has been discussion about supporting "pluggable" host APIs - the general idea is that a client application could link against PortAudio and PortAudio would load the available Host APIs at run-time using "PortAudio Host API Plugins." Some people consider this to be an overly complex solution, and no significant advantages over a monolithic PortAudio dll have been submitted yet. Some people would like PortAudio to always be able to be statically linked with multiple host API support. The ability to load additional host APIs at runtime is considered desirable so long as the basic multiple host API mechanism does not require dynamic linking.
The overhead (both processor and memory) of the Multiple Host API support should be minimised on platforms which don't have multiple host APIs (such as BeOS and some handheld devices.) However, the multiple host API support will still be used on all platoforms, so as to reduce the burden on implementers if a second API becomes available, and also to allow reuse of common code in the multiple host API layer across all implementations.
The implementation will follow the methodology currently employed in PortMIDI described here: http://techweb.rfa.org/pipermail/portaudio/2001-December/000295.html
This proposal has been implemented in the v19-devel CVS branch.
This proposal will involve the changes described below. Note that the string <HA> will be replaced with a host API tag for each implementation.
Each host API will have it's own Initialize function which PortAudio will call in response to client calls to Pa_Initialize and Pa_Terminate respectively. This will be the only identifier each host API implementation will be required to expose.
PaError Pa<HA>_Initialize( PaImplementation **impl );
PaImplementation is an internal data structure containing a set of function pointers for globally relevant functions: (function pointer type declarations omitted for simplicity:)
struct{ fptr terminate; /* takes the PaImplementation* returned by initialize */ fptr getHostAPIInfo; fptr getHostError; fptr getHostErrorText; fptr countDevices; fptr getDefaultInputDeviceID; fptr getDefaultOutputDeviceID; fptr getDeviceInfo; fptr openStream; fptr getMinNumBuffers; } PaImplementation;
The function pointers in PaImplementation will point to the corresponding functions in current PortAudio implementations. The new multiple host API support code will take care of mapping per-host API device ids onto a single homogenous device id range. A significant advantage of this scheme is that it will require very little change to existing PortAudio implementations.
A new PaStreamImplementation internal data structure will be defined to contain function pointers to implementations of the stream functions for each host API. This structure will be placed at the head of implementation-specific data structures returned as PortAudioStream* in current implementations.
struct{ unsigned long magic; fptr close; fptr start; fptr stop; fptr abort; fptr read; fptr write; fptr readAvailable; fptr writeAvailable; fptr active; fptr time; fptr cpuLoad; } PaStreamImplementation;
Magic contains a unique bit pattern which should be set by implementations when a stream is opened, and cleared when it is closed. This technique will allow implementations to perform some degree of validation on PortAudioStream* passed to PortAudio.
This proposal will significantly improve the utility of PortAudio by allowing clients to support multiple host APIs in a single executable.
Since multiple host APIs may return devices with the same names, a minimum requirement for clients who want to be "multiple host API aware" will be to ensure that the appropriate host API name is displayed alongside device names in the user interface.