mirror of https://github.com/mackron/miniaudio.git
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
605 lines
26 KiB
C
605 lines
26 KiB
C
/*
|
|
This is a simple API for low-level audio playback and capture. A reference implementation using
|
|
miniaudio is provided in osaudio.c which can be found alongside this file. Consider all code
|
|
public domain.
|
|
|
|
The idea behind this project came about after considering the absurd complexity of audio APIs on
|
|
various platforms after years of working on miniaudio. This project aims to disprove the idea that
|
|
complete and flexible audio solutions and simple APIs are mutually exclusive and that it's possible
|
|
to have both. The idea of reliability through simplicity is the first and foremost goal of this
|
|
project. The difference between this project and miniaudio is that this project is designed around
|
|
the idea of what I would build if I was building an audio API for an operating system, such as at
|
|
the level of WASAPI or ALSA. A cross-platform and cross-backend library like miniaudio is
|
|
necessarily different in design, but there are indeed things that I would have done differently if
|
|
given my time again, some of those ideas of which I'm expressing in this project.
|
|
|
|
---
|
|
|
|
The concept of low-level audio is simple - you have a device, such as a speaker system or a
|
|
micrphone system, and then you write or read audio data to/from it. So in the case of playback, you
|
|
need only write your raw audio data to the device which then emits it from the speakers when it's
|
|
ready. Likewise, for capture you simply read audio data from the device which is filled with data
|
|
by the microphone.
|
|
|
|
A complete low-level audio solution requires the following:
|
|
|
|
1) The ability to enumerate devices that are connected to the system.
|
|
2) The ability to open and close a connection to a device.
|
|
3) The ability to start and stop the device.
|
|
4) The ability to write and read audio data to/from the device.
|
|
5) The ability to query the device for it's data configuration.
|
|
6) The ability to notify the application when certain events occur, such as the device being
|
|
stopped, or rerouted.
|
|
|
|
The API presented here aims to meet all of the above requirements. It uses a single-threaded
|
|
blocking read/write model for data delivery instead of a callback model. This makes it a bit more
|
|
flexible since it gives the application full control over the audio thread. It might also make it
|
|
more feasible to use this API on single-threaded systems.
|
|
|
|
Device enumeration is achieved with a single function: osaudio_enumerate(). This function returns
|
|
an array of osaudio_info_t structures which contain information about each device. The array is
|
|
allocated must be freed with free(). Contained within the osaudio_info_t struct is, most
|
|
importantly, the device ID, which is used to open a connection to the device, and the name of the
|
|
device which can be used to display to the user. For advanced users, it also includes information
|
|
about the device's native data configuration.
|
|
|
|
Opening and closing a connection to a device is achieved with osaudio_open() and osaudio_close().
|
|
An important concept is that of the ability to configure the device. This is achieved with the
|
|
osaudio_config_t structure which is passed to osaudio_open(). In addition to the ID of the device,
|
|
this structure includes information about the desired format, channel count and sample rate. You
|
|
can also configure the latency of the device, or the buffer size, which is specified in frames. A
|
|
flags member is used for specifying additional options, such as whether or not to disable automatic
|
|
rerouting. Finally a callback can be specified for notifications. When osaudio_open() returns, the
|
|
config structure will be filled with the device's actual configuration. You can inspect the channel
|
|
map from this structure to know how to arrange the channels in your audio data.
|
|
|
|
This API uses a blocking write/read model for pushing and pulling data to/from the device. This
|
|
is done with the osaudio_write() and osaudio_read() functions. These functions will block until
|
|
the requested number of frames have been processed or the device is drained or flushed with
|
|
osaudio_drain() or osaudio_flush() respectively. It is from these functions that the device is
|
|
started. As soon as you start writing data with osaudio_write() or reading data with
|
|
osaudio_read(), the device will start. When the device is drained of flushed with osaudio_drain()
|
|
or osaudio_flush(), the device will be stopped. osaudio_drain() will block until the device has
|
|
been drained, whereas osaudio_flush() will stop playback immediately and return. You can also pause
|
|
and resume the device with osaudio_pause() and osaudio_resume(). Since reading and writing is
|
|
blocking, it can be useful to know how many frames can be written/read without blocking. This is
|
|
achieved with osaudio_get_avail().
|
|
|
|
Querying the device's configuration is achieved with osaudio_get_info(). This function will return
|
|
a pointer to a osaudio_info_t structure which contains information about the device, most
|
|
importantly it's name and data configuration. The name is important for displaying on a UI, and
|
|
the data configuration is important for knowing how to format your audio data. The osaudio_info_t
|
|
structure will contain an array of osaudio_config_t structures. This will contain one entry, which
|
|
will contain the exact information that was returned in the config structure that was passed to
|
|
osaudio_open().
|
|
|
|
A common requirement is to open a device that represents the operating system's default device.
|
|
This is done easily by simply passing in NULL for the device ID. Below is an example for opening a
|
|
default device:
|
|
|
|
int result;
|
|
osaudio_t audio;
|
|
osaudio_config_t config;
|
|
|
|
osaudio_config_init(&config, OSAUDIO_OUTPUT);
|
|
config.format = OSAUDIO_FORMAT_F32;
|
|
config.channels = 2;
|
|
config.rate = 48000;
|
|
|
|
result = osaudio_open(&audio, &config);
|
|
if (result != OSAUDIO_SUCCESS) {
|
|
printf("Failed to open device.");
|
|
return -1;
|
|
}
|
|
|
|
...
|
|
|
|
osaudio_close(audio);
|
|
|
|
In the above example, the default device is opened for playback (OSAUDIO_OUTPUT). The format is
|
|
set to 32-bit floating point (OSAUDIO_FORMAT_F32), the channel count is set to stereo (2), and the
|
|
sample rate is set to 48kHz. The device is then closed when we're done with it.
|
|
|
|
If instead we wanted to open a specific device, we can do that by passing in the device ID. Below
|
|
is an example for how to do this:
|
|
|
|
int result;
|
|
osaudio_t audio;
|
|
osaudio_config_t config;
|
|
unsigned int infoCount;
|
|
osaudio_info_t* info;
|
|
|
|
result = osaudio_enumerate(&infoCount, &info);
|
|
if (result != OSAUDIO_SUCCESS) {
|
|
printf("Failed to enumerate devices.\n");
|
|
return -1;
|
|
}
|
|
|
|
// ... Iterate over the `info` array and find the device you want to open. Use the `direction` member to discriminate between input and output ...
|
|
|
|
osaudio_config_init(&config, OSAUDIO_OUTPUT);
|
|
config.id = &info[indexOfYourChosenDevice].id;
|
|
config.format = OSAUDIO_FORMAT_F32;
|
|
config.channels = 2;
|
|
config.rate = 48000;
|
|
|
|
osaudio_open(&audio, &config);
|
|
|
|
...
|
|
|
|
osaudio_close(audio);
|
|
free(info); // The pointer returned by osaudio_enumerate() must be freed with free().
|
|
|
|
The id structure is just a 256 byte array that uniquely identifies the device. Implementations may
|
|
have different representations for device IDs, and A 256 byte array should accomodates all
|
|
device ID representations. Implementations are required to zero-fill unused bytes. The osaudio_id_t
|
|
structure can be copied which makes it suitable for serialization and deserialization in situations
|
|
where you may want to save the device ID to permanent storage so it can be stored in a config file.
|
|
|
|
Implementations need to do their own data conversion between the device's native data configuration
|
|
and the requested configuration. In this case, when the format, channels and rate are specified in
|
|
the config, they should be unchanged when osaudio_open() returns. If this is not possible,
|
|
osaudio_open() will return OSAUDIO_FORMAT_NOT_SUPPORTED. However, there are cases where it's useful
|
|
for a program to use the device's native configuration instead of some fixed configuration. This is
|
|
achieved by setting the format, channels and rate to 0. Below is an example:
|
|
|
|
int result;
|
|
osaudio_t audio;
|
|
osaudio_config_t config;
|
|
|
|
osaudio_config_init(&config, OSAUDIO_OUTPUT);
|
|
|
|
result = osaudio_open(&audio, &config);
|
|
if (result != OSAUDIO_SUCCESS) {
|
|
printf("Failed to open device.");
|
|
return -1;
|
|
}
|
|
|
|
// ... `config` will have been updated by osaudio_open() to contain the *actual* format/channels/rate ...
|
|
|
|
osaudio_close(audio);
|
|
|
|
In addition to the code above, you can explicitly call `osaudio_get_info()` to retrieve the format
|
|
configuration. If you need to know the native configuration before opening the device, you can use
|
|
enumeration. The format, channels and rate will be contined in the first item in the configs array.
|
|
|
|
The examples above all use playback, but the same applies for capture. The only difference is that
|
|
the direction is set to OSAUDIO_INPUT instead of OSAUDIO_OUTPUT.
|
|
|
|
To output audio from the speakers you need to call osaudio_write(). Likewise, to capture audio from
|
|
a microphone you need to call osaudio_read(). These functions will block until the requested number
|
|
of frames have been written or read. The device will start automatically. Below is an example for
|
|
writing some data to a device:
|
|
|
|
int result = osaudio_write(audio, myAudioData, myAudioDataFrameCount);
|
|
if (result == OSAUDIO_SUCCESS) {
|
|
printf("Successfully wrote %d frames of audio data.\n", myAudioDataFrameCount);
|
|
} else {
|
|
printf("Failed to write audio data.\n");
|
|
}
|
|
|
|
osaudio_write() and osaudio_read() will return OSAUDIO_SUCCESS if the requested number of frames
|
|
were written or read. You cannot call osaudio_close() while a write or read operation is in
|
|
progress.
|
|
|
|
If you want to write or read audio data without blocking, you can use osaudio_get_avail() to
|
|
determine how many frames are available for writing or reading. Below is an example:
|
|
|
|
unsigned int framesAvailable = osaudio_get_avail(audio);
|
|
if (result > 0) {
|
|
printf("There are %d frames available for writing.\n", framesAvailable);
|
|
} else {
|
|
printf("There are no frames available for writing.\n");
|
|
}
|
|
|
|
If you want to abort a blocking write or read, you can use osaudio_flush(). This will result in any
|
|
pending write or read operation being aborted.
|
|
|
|
There are several ways of pausing a device. The first is to just drain or flush the device and
|
|
simply don't do any more read/write operations. A drain and flush will put the device into a
|
|
stopped state until the next call to either read or write, depending on the device's direction.
|
|
If, however, this does not suit your requirements, you can use osaudio_pause() and
|
|
osaudio_resume(). Take note, however, that these functions will result in osaudio_drain() never
|
|
returning because it'll result in the device being in a stopped state which in turn results in the
|
|
buffer never being read and therefore never drained.
|
|
|
|
Everything is thread safe with a few minor exceptions which has no practical issues for the client:
|
|
|
|
* You cannot call any function while osaudio_open() is still in progress.
|
|
* You cannot call osaudio_close() while any other function is still in progress.
|
|
* You can only call osaudio_write() and osaudio_read() from one thread at a time.
|
|
|
|
None of these issues should be a problem for the client in practice. You won't have a valid
|
|
osaudio_t object until osaudio_open() has returned. For osaudio_close(), it makes no sense to
|
|
destroy the object while it's still in use, and doing so would mean the client is using very poor
|
|
form. For osaudio_write() and osaudio_read(), you wouldn't ever want to call this simultaneously
|
|
across multiple threads anyway because otherwise you'd end up with garbage audio.
|
|
|
|
The rules above only apply when working with a single osaudio_t object. You can have multiple
|
|
osaudio_t objects open at the same time, and you can call any function on different osaudio_t
|
|
objects simultaneously from different threads.
|
|
|
|
---
|
|
|
|
# Feedback
|
|
|
|
I'm looking for feedback on the following:
|
|
|
|
* Are the supported formats enough? If not, what other formats are needed, and what is the
|
|
justification for including it? Just because it's the native format on one particular
|
|
piece of hardware is not enough. Big-endian and little-endian will never be supported. All
|
|
formats are native-endian.
|
|
* Are the available channel positions enough? What other positions are needed?
|
|
* Just some general criticism would be appreciated.
|
|
|
|
*/
|
|
#ifndef osaudio_h
|
|
#define osaudio_h
|
|
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif
|
|
|
|
/*
|
|
Support far pointers on relevant platforms (DOS, in particular). The version of this file
|
|
distributed with an operating system wouldn't need this because they would just have an
|
|
OS-specific version of this file, but as a reference it's useful to use far pointers here.
|
|
*/
|
|
#if defined(__MSDOS__) || defined(_MSDOS) || defined(__DOS__)
|
|
#define OSAUDIO_FAR far
|
|
#else
|
|
#define OSAUDIO_FAR
|
|
#endif
|
|
|
|
typedef struct _osaudio_t* osaudio_t;
|
|
typedef struct osaudio_config_t osaudio_config_t;
|
|
typedef struct osaudio_id_t osaudio_id_t;
|
|
typedef struct osaudio_info_t osaudio_info_t;
|
|
typedef struct osaudio_notification_t osaudio_notification_t;
|
|
|
|
/* Results codes. */
|
|
typedef int osaudio_result_t;
|
|
#define OSAUDIO_SUCCESS 0
|
|
#define OSAUDIO_ERROR -1
|
|
#define OSAUDIO_INVALID_ARGS -2
|
|
#define OSAUDIO_INVALID_OPERATION -3
|
|
#define OSAUDIO_OUT_OF_MEMORY -4
|
|
#define OSAUDIO_FORMAT_NOT_SUPPORTED -101 /* The requested format is not supported. */
|
|
#define OSAUDIO_XRUN -102 /* An underrun or overrun occurred. Can be returned by osaudio_read() or osaudio_write(). */
|
|
#define OSAUDIO_DEVICE_STOPPED -103 /* The device is stopped. Can be returned by osaudio_drain(). It is invalid to call osaudio_drain() on a device that is not running because otherwise it'll get stuck. */
|
|
|
|
/* Directions. Cannot be combined. Use separate osaudio_t objects for birectional setups. */
|
|
typedef int osaudio_direction_t;
|
|
#define OSAUDIO_INPUT 1
|
|
#define OSAUDIO_OUTPUT 2
|
|
|
|
/* All formats are native endian and interleaved. */
|
|
typedef int osaudio_format_t;
|
|
#define OSAUDIO_FORMAT_UNKNOWN 0
|
|
#define OSAUDIO_FORMAT_F32 1
|
|
#define OSAUDIO_FORMAT_U8 2
|
|
#define OSAUDIO_FORMAT_S16 3
|
|
#define OSAUDIO_FORMAT_S24 4 /* Tightly packed. */
|
|
#define OSAUDIO_FORMAT_S32 5
|
|
|
|
/* Channel positions. */
|
|
typedef unsigned char osaudio_channel_t;
|
|
#define OSAUDIO_CHANNEL_NONE 0
|
|
#define OSAUDIO_CHANNEL_MONO 1
|
|
#define OSAUDIO_CHANNEL_FL 2
|
|
#define OSAUDIO_CHANNEL_FR 3
|
|
#define OSAUDIO_CHANNEL_FC 4
|
|
#define OSAUDIO_CHANNEL_LFE 5
|
|
#define OSAUDIO_CHANNEL_BL 6
|
|
#define OSAUDIO_CHANNEL_BR 7
|
|
#define OSAUDIO_CHANNEL_FLC 8
|
|
#define OSAUDIO_CHANNEL_FRC 9
|
|
#define OSAUDIO_CHANNEL_BC 10
|
|
#define OSAUDIO_CHANNEL_SL 11
|
|
#define OSAUDIO_CHANNEL_SR 12
|
|
#define OSAUDIO_CHANNEL_TC 13
|
|
#define OSAUDIO_CHANNEL_TFL 14
|
|
#define OSAUDIO_CHANNEL_TFC 15
|
|
#define OSAUDIO_CHANNEL_TFR 16
|
|
#define OSAUDIO_CHANNEL_TBL 17
|
|
#define OSAUDIO_CHANNEL_TBC 18
|
|
#define OSAUDIO_CHANNEL_TBR 19
|
|
#define OSAUDIO_CHANNEL_AUX0 20
|
|
#define OSAUDIO_CHANNEL_AUX1 21
|
|
#define OSAUDIO_CHANNEL_AUX2 22
|
|
#define OSAUDIO_CHANNEL_AUX3 23
|
|
#define OSAUDIO_CHANNEL_AUX4 24
|
|
#define OSAUDIO_CHANNEL_AUX5 25
|
|
#define OSAUDIO_CHANNEL_AUX6 26
|
|
#define OSAUDIO_CHANNEL_AUX7 27
|
|
#define OSAUDIO_CHANNEL_AUX8 28
|
|
#define OSAUDIO_CHANNEL_AUX9 29
|
|
#define OSAUDIO_CHANNEL_AUX10 30
|
|
#define OSAUDIO_CHANNEL_AUX11 31
|
|
#define OSAUDIO_CHANNEL_AUX12 32
|
|
#define OSAUDIO_CHANNEL_AUX13 33
|
|
#define OSAUDIO_CHANNEL_AUX14 34
|
|
#define OSAUDIO_CHANNEL_AUX15 35
|
|
#define OSAUDIO_CHANNEL_AUX16 36
|
|
#define OSAUDIO_CHANNEL_AUX17 37
|
|
#define OSAUDIO_CHANNEL_AUX18 38
|
|
#define OSAUDIO_CHANNEL_AUX19 39
|
|
#define OSAUDIO_CHANNEL_AUX20 40
|
|
#define OSAUDIO_CHANNEL_AUX21 41
|
|
#define OSAUDIO_CHANNEL_AUX22 42
|
|
#define OSAUDIO_CHANNEL_AUX23 43
|
|
#define OSAUDIO_CHANNEL_AUX24 44
|
|
#define OSAUDIO_CHANNEL_AUX25 45
|
|
#define OSAUDIO_CHANNEL_AUX26 46
|
|
#define OSAUDIO_CHANNEL_AUX27 47
|
|
#define OSAUDIO_CHANNEL_AUX28 48
|
|
#define OSAUDIO_CHANNEL_AUX29 49
|
|
#define OSAUDIO_CHANNEL_AUX30 50
|
|
#define OSAUDIO_CHANNEL_AUX31 51
|
|
|
|
/* The maximum number of channels supported. */
|
|
#define OSAUDIO_MAX_CHANNELS 64
|
|
|
|
/* Notification types. */
|
|
typedef int osaudio_notification_type_t;
|
|
#define OSAUDIO_NOTIFICATION_STARTED 0 /* The device was started in response to a call to osaudio_write() or osaudio_read(). */
|
|
#define OSAUDIO_NOTIFICATION_STOPPED 1 /* The device was stopped in response to a call to osaudio_drain() or osaudio_flush(). */
|
|
#define OSAUDIO_NOTIFICATION_REROUTED 2 /* The device was rerouted. Not all implementations need to support rerouting. */
|
|
#define OSAUDIO_NOTIFICATION_INTERRUPTION_BEGIN 3 /* The device was interrupted due to something like a phone call. */
|
|
#define OSAUDIO_NOTIFICATION_INTERRUPTION_END 4 /* The interruption has been ended. */
|
|
|
|
/* Flags. */
|
|
#define OSAUDIO_FLAG_NO_REROUTING 1 /* When set, will tell the implementation to disable automatic rerouting if possible. This is a hint and may be ignored by the implementation. */
|
|
#define OSAUDIO_FLAG_REPORT_XRUN 2 /* When set, will tell the implementation to report underruns and overruns via osaudio_write() and osaudio_read() by aborting and returning OSAUDIO_XRUN. */
|
|
|
|
struct osaudio_notification_t
|
|
{
|
|
osaudio_notification_type_t type; /* OSAUDIO_NOTIFICATION_* */
|
|
union
|
|
{
|
|
struct
|
|
{
|
|
int _unused;
|
|
} started;
|
|
struct
|
|
{
|
|
int _unused;
|
|
} stopped;
|
|
struct
|
|
{
|
|
int _unused;
|
|
} rerouted;
|
|
struct
|
|
{
|
|
int _unused;
|
|
} interruption;
|
|
} data;
|
|
};
|
|
|
|
struct osaudio_id_t
|
|
{
|
|
char data[256];
|
|
};
|
|
|
|
struct osaudio_config_t
|
|
{
|
|
osaudio_id_t* device_id; /* Set to NULL to use default device. When non-null, automatic routing will be disabled. */
|
|
osaudio_direction_t direction; /* OSAUDIO_INPUT or OSAUDIO_OUTPUT. Cannot be combined. Use separate osaudio_t objects for bidirectional setups. */
|
|
osaudio_format_t format; /* OSAUDIO_FORMAT_* */
|
|
unsigned int channels; /* Number of channels. */
|
|
unsigned int rate; /* Sample rate in seconds. */
|
|
osaudio_channel_t channel_map[OSAUDIO_MAX_CHANNELS]; /* Leave all items set to 0 for defaults. */
|
|
unsigned int buffer_size; /* In frames. Set to 0 to use the system default. */
|
|
unsigned int flags; /* A combination of OSAUDIO_FLAG_* */
|
|
void (* notification)(void* user_data, const osaudio_notification_t* notification); /* Called when some kind of event occurs, such as a device being closed. Never called from the audio thread. */
|
|
void* user_data; /* Passed to notification(). */
|
|
};
|
|
|
|
struct osaudio_info_t
|
|
{
|
|
osaudio_id_t id;
|
|
char name[256];
|
|
osaudio_direction_t direction; /* OSAUDIO_INPUT or OSAUDIO_OUTPUT. */
|
|
unsigned int config_count;
|
|
osaudio_config_t* configs;
|
|
};
|
|
|
|
|
|
/*
|
|
Enumerates the available devices.
|
|
|
|
On output, `count` will contain the number of items in the `info` array. The array must be freed
|
|
with free() when it's no longer needed.
|
|
|
|
Use the `direction` member to discriminate between input and output devices. Below is an example:
|
|
|
|
unsigned int count;
|
|
osaudio_info_t* info;
|
|
osaudio_enumerate(&count, &info);
|
|
|
|
for (int i = 0; i < count; ++i) {
|
|
if (info[i].direction == OSAUDIO_OUTPUT) {
|
|
printf("Output device: %s\n", info[i].name);
|
|
} else {
|
|
printf("Input device: %s\n", info[i].name);
|
|
}
|
|
}
|
|
|
|
You can use the `id` member to open a specific device with osaudio_open(). You do not need to do
|
|
device enumeration if you only want to open the default device.
|
|
*/
|
|
osaudio_result_t osaudio_enumerate(unsigned int* count, osaudio_info_t** info);
|
|
|
|
/*
|
|
Initializes a default config.
|
|
|
|
The config object will be cleared to zero, with the direction set to `direction`. This will result
|
|
in a configuration that uses the device's native format, channels and rate.
|
|
|
|
osaudio_config_t is a transparent struct. Just set the relevant fields to the desired values after
|
|
calling this function. Example:
|
|
|
|
osaudio_config_t config;
|
|
osaudio_config_init(&config, OSAUDIO_OUTPUT);
|
|
config.format = OSAUDIO_FORMAT_F32;
|
|
config.channels = 2;
|
|
config.rate = 48000;
|
|
*/
|
|
void osaudio_config_init(osaudio_config_t* config, osaudio_direction_t direction);
|
|
|
|
/*
|
|
Opens a connection to a device.
|
|
|
|
On input, config must be filled with the desired configuration. On output, it will be filled with
|
|
the actual configuration.
|
|
|
|
Initialize the config with osaudio_config_init() and then fill in the desired configuration. Below
|
|
is an example:
|
|
|
|
osaudio_config_t config;
|
|
osaudio_config_init(&config, OSAUDIO_OUTPUT);
|
|
config.format = OSAUDIO_FORMAT_F32;
|
|
config.channels = 2;
|
|
config.rate = 48000;
|
|
|
|
When the format, channels or rate are left at their default values, or set to 0 (or
|
|
OSAUDIO_FORMAT_UNKNOWN for format), the native format, channels or rate will use the device's
|
|
native configuration:
|
|
|
|
osaudio_config_t config;
|
|
osaudio_config_init(&config, OSAUDIO_OUTPUT);
|
|
config.format = OSAUDIO_FORMAT_UNKNOWN;
|
|
config.channels = 0;
|
|
config.rate = 0;
|
|
|
|
The code above is equivalent to this:
|
|
|
|
osaudio_config_t config;
|
|
osaudio_config_init(&config, OSAUDIO_OUTPUT);
|
|
|
|
On output the config will be filled with the actual configuration. The implementation will perform
|
|
any necessary data conversion between the requested data configuration and the device's native
|
|
configuration. If it cannot, the function will return a OSAUDIO_FORMAT_NOT_SUPPORTED error. In this
|
|
case the caller can decide to reinitialize the device to use it's native configuration and do it's
|
|
own data conversion, or abort if it cannot do so. Use the channel map to determine the ordering of
|
|
your channels. Automatic channel map conversion is not performed - that must be done manually by
|
|
the caller when transfering data to/from the device.
|
|
|
|
Close the device with osaudio_close().
|
|
|
|
Returns 0 on success, any other error code on failure.
|
|
*/
|
|
osaudio_result_t osaudio_open(osaudio_t* audio, osaudio_config_t* config);
|
|
|
|
/*
|
|
Closes a connection to a device.
|
|
|
|
As soon as this function is called, the device should be considered invalid and unsuable. Do not
|
|
attempt to use the audio object once this function has been called.
|
|
|
|
It's invalid to call this while any other function is still running. You can use osaudio_flush() to
|
|
quickly abort any pending writes or reads. You can also use osaudio_drain() to wait for all pending
|
|
writes or reads to complete.
|
|
|
|
Returns 0 on success, < 0 on failure.
|
|
*/
|
|
osaudio_result_t osaudio_close(osaudio_t audio);
|
|
|
|
/*
|
|
Writes audio data to the device.
|
|
|
|
This will block until all data has been written or the device is closed.
|
|
|
|
You can only write from a single thread at any given time. If you want to write from multiple
|
|
threads, you need to use your own synchronization mechanism.
|
|
|
|
This will automatically start the device if frame_count is > 0 and it's not in a paused state.
|
|
|
|
Use osaudio_get_avail() to determine how much data can be written without blocking.
|
|
|
|
Returns 0 on success, < 0 on failure.
|
|
*/
|
|
osaudio_result_t osaudio_write(osaudio_t audio, const void OSAUDIO_FAR* data, unsigned int frame_count);
|
|
|
|
/*
|
|
Reads audio data from the device.
|
|
|
|
This will block until the requested number of frames has been read or the device is closed.
|
|
|
|
You can only read from a single thread at any given time. If you want to read from multiple
|
|
threads, you need to use your own synchronization mechanism.
|
|
|
|
This will automatically start the device if frame_count is > 0 and it's not in a paused state.
|
|
|
|
Use osaudio_get_avail() to determine how much data can be read without blocking.
|
|
|
|
Returns 0 on success, < 0 on failure.
|
|
*/
|
|
osaudio_result_t osaudio_read(osaudio_t audio, void OSAUDIO_FAR* data, unsigned int frame_count);
|
|
|
|
/*
|
|
Drains the device.
|
|
|
|
This will block until all pending reads or writes have completed.
|
|
|
|
If after calling this function another call to osaudio_write() or osaudio_read() is made, the
|
|
device will be resumed like normal.
|
|
|
|
It is invalid to call this while the device is paused.
|
|
|
|
Returns 0 on success, < 0 on failure.
|
|
*/
|
|
osaudio_result_t osaudio_drain(osaudio_t audio);
|
|
|
|
/*
|
|
Flushes the device.
|
|
|
|
This will immediately flush any pending reads or writes. It will not block. Any in-progress reads
|
|
or writes will return immediately.
|
|
|
|
If after calling this function another thread starts reading or writing, the device will be resumed
|
|
like normal.
|
|
|
|
Returns 0 on success, < 0 on failure.
|
|
*/
|
|
osaudio_result_t osaudio_flush(osaudio_t audio);
|
|
|
|
/*
|
|
Pauses or resumes the device.
|
|
|
|
Pausing a device will trigger a OSAUDIO_NOTIFICATION_STOPPED notification. Resuming a device will
|
|
trigger a OSAUDIO_NOTIFICATION_STARTED notification.
|
|
|
|
Returns 0 on success, < 0 on failure.
|
|
*/
|
|
osaudio_result_t osaudio_pause(osaudio_t audio);
|
|
|
|
/*
|
|
Resumes the device.
|
|
|
|
Returns 0 on success, < 0 on failure.
|
|
*/
|
|
osaudio_result_t osaudio_resume(osaudio_t audio);
|
|
|
|
/*
|
|
Returns the number of frames that can be read or written without blocking.
|
|
*/
|
|
unsigned int osaudio_get_avail(osaudio_t audio);
|
|
|
|
/*
|
|
Gets information about the device.
|
|
|
|
There will be one item in the configs array which will contain the device's current configuration,
|
|
the contents of which will match that of the config that was returned by osaudio_open().
|
|
|
|
Returns NULL on failure. Do not free the returned pointer. It's up to the implementation to manage
|
|
the meory of this object.
|
|
*/
|
|
const osaudio_info_t* osaudio_get_info(osaudio_t audio);
|
|
|
|
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif
|
|
#endif /* osaudio_h */
|