document possible errors

This commit is contained in:
Andrew Kelley 2015-08-26 15:17:51 -07:00
parent c96405a091
commit a0562af122
6 changed files with 155 additions and 35 deletions

View file

@ -8,8 +8,6 @@ This library is an abstraction; however in the delicate balance between
performance and power, and API convenience, the scale is tipped closer to
the former. Features that only exist in some sound backends are exposed.
[API Documentation](http://libsound.io/doc)
**This project is a work-in-progress.**
## Features and Limitations
@ -295,22 +293,13 @@ Then look at `html/index.html` in a browser.
0. Create a test for input stream overflow handling.
0. Allow calling functions from outside the callbacks as long as they first
call lock and then unlock when done.
0. add len arguments to APIs that have char *
- replace strdup with `soundio_str_dupe`
0. write detailed docs on buffer underflows explaining when they occur, what state
changes are related to them, and how to recover from them.
0. Consider testing on FreeBSD
0. In ALSA do we need to wake up the poll when destroying the in or out stream?
0. Detect PulseAudio server going offline and emit `on_backend_disconnect`.
0. Custom allocator support
- default allocator mlock memory
0. Support for stream icon.
- PulseAudio: XDG icon name
- WASAPI: path to .exe, .dll, or .ico
- CoreAudio: CFURLRef image file
0. clean up API and improve documentation
- make sure every function which can return an error documents which errors
it can return
## Planned Uses for libsoundio

View file

@ -1665,7 +1665,8 @@ INCLUDE_FILE_PATTERNS =
# recursively expanded use the := operator instead of the = operator.
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
PREDEFINED =
PREDEFINED = SOUNDIO_EXTERN_C= \
SOUNDIO_EXPORT= \
# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
# tag can be used to specify a list of macro names that should be expanded. The

View file

@ -11,6 +11,7 @@
#include "config.h"
#include <stdbool.h>
/// \cond
#ifdef __cplusplus
#define SOUNDIO_EXTERN_C extern "C"
#else
@ -26,6 +27,7 @@
#else
#define SOUNDIO_EXPORT SOUNDIO_EXTERN_C __attribute__((visibility ("default")))
#endif
/// \endcond
/** \mainpage
*
@ -56,20 +58,34 @@
/// See also ::soundio_strerror
enum SoundIoError {
SoundIoErrorNone,
/// Out of memory.
SoundIoErrorNoMem,
/// The backend does not appear to be active or running.
SoundIoErrorInitAudioBackend,
/// A system resource other than memory was not available.
SoundIoErrorSystemResources,
/// Attempted to open a device and failed.
SoundIoErrorOpeningDevice,
SoundIoErrorNoSuchDevice,
/// The programmer did not comply with the API.
SoundIoErrorInvalid,
/// libsoundio was compiled without support for that backend.
SoundIoErrorBackendUnavailable,
/// An open stream had an error that can only be recovered from by
/// destroying the stream and creating it again.
SoundIoErrorStreaming,
/// Attempted to use a device with parameters it cannot support.
SoundIoErrorIncompatibleDevice,
/// When JACK returns `JackNoSuchClient`
SoundIoErrorNoSuchClient,
/// Attempted to use parameters that the backend cannot support.
SoundIoErrorIncompatibleBackend,
/// Backend server shutdown or became inactive.
SoundIoErrorBackendDisconnected,
SoundIoErrorInterrupted,
/// Buffer underrun occurred.
SoundIoErrorUnderflow,
/// Unable to convert to or from UTF-8 to the native string format.
SoundIoErrorEncodingString,
};
@ -203,6 +219,8 @@ enum SoundIoDeviceAim {
SoundIoDeviceAimOutput, ///< playback
};
/// For your convenience, Native Endian and Foreign Endian constants are defined
/// which point to the respective SoundIoFormat values.
enum SoundIoFormat {
SoundIoFormatInvalid,
SoundIoFormatS8, ///< Signed 8 bit
@ -225,9 +243,6 @@ enum SoundIoFormat {
SoundIoFormatFloat64BE, ///< Float 64 bit Big Endian, Range -1.0 to 1.0
};
// For your convenience, Native Endian and Foreign Endian constants are defined
// which point to the respective SoundIoFormat values.
#if defined(SOUNDIO_OS_BIG_ENDIAN)
#define SoundIoFormatS16NE SoundIoFormatS16BE
#define SoundIoFormatU16NE SoundIoFormatU16BE
@ -446,6 +461,10 @@ struct SoundIoDevice {
/// fields of the device will be populated. If there is an error code here
/// then information about formats, sample rates, and channel layouts might
/// be missing.
///
/// Possible errors:
/// * #SoundIoErrorOpeningDevice
/// * #SoundIoErrorNoMem
int probe_error;
};
@ -454,7 +473,7 @@ struct SoundIoOutStream {
/// Populated automatically when you call ::soundio_outstream_create.
struct SoundIoDevice *device;
/// Defaults to SoundIoFormatFloat32NE, followed by the first one supported.
/// Defaults to #SoundIoFormatFloat32NE, followed by the first one supported.
enum SoundIoFormat format;
/// Sample rate is the number of frames per second.
@ -605,18 +624,31 @@ struct SoundIoInStream {
// Main Context
/// Create a SoundIo context. You may create multiple instances of this to
/// connect to multiple backends.
/// connect to multiple backends. Sets all fields to defaults.
/// Returns `NULL` if and only if memory could not be allocated.
/// See also ::soundio_destroy
SOUNDIO_EXPORT struct SoundIo * soundio_create(void);
SOUNDIO_EXPORT struct SoundIo *soundio_create(void);
SOUNDIO_EXPORT void soundio_destroy(struct SoundIo *soundio);
/// This is a convenience function you could implement yourself if you wanted
/// to. It tries ::soundio_connect_backend on all available backends in order.
/// Tries ::soundio_connect_backend on all available backends in order.
/// Possible errors:
/// * #SoundIoErrorInvalid - already connected
/// * #SoundIoErrorNoMem
/// * #SoundIoErrorSystemResources
/// * #SoundIoErrorNoSuchClient - when JACK returns `JackNoSuchClient`
/// See also ::soundio_disconnect
SOUNDIO_EXPORT int soundio_connect(struct SoundIo *soundio);
/// Instead of calling ::soundio_connect you may call this function to try a
/// specific backend.
/// Possible errors:
/// * #SoundIoErrorInvalid - already connected or invalid backend parameter
/// * #SoundIoErrorNoMem
/// * #SoundIoErrorBackendUnavailable - backend was not compiled in
/// * #SoundIoErrorSystemResources
/// * #SoundIoErrorNoSuchClient - when JACK returns `JackNoSuchClient`
/// * #SoundIoErrorInitAudioBackend - requested `backend` is not active
/// * #SoundIoErrorBackendDisconnected - backend disconnected while connecting
/// See also ::soundio_disconnect
SOUNDIO_EXPORT int soundio_connect_backend(struct SoundIo *soundio, enum SoundIoBackend backend);
SOUNDIO_EXPORT void soundio_disconnect(struct SoundIo *soundio);
@ -797,7 +829,9 @@ SOUNDIO_EXPORT int soundio_device_nearest_sample_rate(struct SoundIoDevice *devi
// Output Streams
/// Allocates memory and sets defaults. Next you should fill out the struct fields
/// and then call ::soundio_outstream_open.
/// and then call ::soundio_outstream_open. Sets all fields to defaults.
/// Returns `NULL` if and only if memory could not be allocated.
/// See also ::soundio_outstream_destroy
SOUNDIO_EXPORT struct SoundIoOutStream *soundio_outstream_create(struct SoundIoDevice *device);
/// You may not call this function from the SoundIoOutStream::write_callback thread context.
SOUNDIO_EXPORT void soundio_outstream_destroy(struct SoundIoOutStream *outstream);
@ -807,15 +841,37 @@ SOUNDIO_EXPORT void soundio_outstream_destroy(struct SoundIoOutStream *outstream
/// The next thing to do is call ::soundio_instream_start.
/// If this function returns an error, the outstream is in an invalid state and
/// you must call ::soundio_outstream_destroy on it.
///
/// Possible errors:
/// * #SoundIoErrorInvalid
/// * SoundIoDevice::aim is not #SoundIoDeviceAimOutput
/// * SoundIoOutStream::format is not valid
/// * SoundIoOutStream::channel_count is greater than #SOUNDIO_MAX_CHANNELS
/// * #SoundIoErrorNoMem
/// * #SoundIoErrorOpeningDevice
/// * #SoundIoErrorBackendDisconnected
/// * #SoundIoErrorSystemResources
/// * #SoundIoErrorNoSuchClient - when JACK returns `JackNoSuchClient`
/// * #SoundIoErrorOpeningDevice
/// * #SoundIoErrorIncompatibleBackend - SoundIoOutStream::channel_count is
/// greater than the number of channels the backend can handle.
/// * #SoundIoErrorIncompatibleDevice - stream parameters requested are not
/// compatible with the chosen device.
SOUNDIO_EXPORT int soundio_outstream_open(struct SoundIoOutStream *outstream);
/// After you call this function, SoundIoOutStream::write_callback will be called.
///
/// Possible errors:
/// * #SoundIoErrorStreaming
/// * #SoundIoErrorNoMem
/// * #SoundIoErrorSystemResources
/// * #SoundIoErrorBackendDisconnected
SOUNDIO_EXPORT int soundio_outstream_start(struct SoundIoOutStream *outstream);
/// Call this function when you are ready to begin writing to the device buffer.
/// * `outstream` - (in) The output stream you want to write to.
/// * `areas` - (out) The memory addresses you can write data to. It is OK to
/// modify the pointers if that helps you iterate.
/// * `areas` - (out) The memory addresses you can write data to, one per
/// channel. It is OK to modify the pointers if that helps you iterate.
/// * `frame_count` - (in/out) Provide the number of frames you want to write.
/// Returned will be the number of frames you can actually write, which is
/// also the number of frames that will be written when you call
@ -827,19 +883,40 @@ SOUNDIO_EXPORT int soundio_outstream_start(struct SoundIoOutStream *outstream);
/// You must call this function only from the SoundIoOutStream::write_callback thread context.
/// After calling this function, write data to `areas` and then call
/// ::soundio_outstream_end_write.
/// If you call this function with `frame_count` less than the `frame_count_min`
/// parameter from SoundIoOutStream::write_callback it returns SoundIoErrorInvalid.
/// If this function returns an error, do not call ::soundio_outstream_end_write.
///
/// Possible errors:
/// * #SoundIoErrorInvalid
/// * `*frame_count` <= 0
/// * `*frame_count` < `frame_count_min` or `*frame_count` > `frame_count_max`
/// * function called too many times without respecting `frame_count_max`
/// * #SoundIoErrorStreaming
/// * #SoundIoErrorUnderflow - an underflow caused this call to fail. You might
/// also get a SoundIoOutStream::underflow_callback, and you might not get
/// this error code when an underflow occurs. Unlike #SoundIoErrorStreaming,
/// the outstream is still in a valid state and streaming can continue.
/// * #SoundIoErrorIncompatibleDevice - in rare cases it might just now
/// be discovered that the device uses non-byte-aligned access, in which
/// case this error code is returned.
SOUNDIO_EXPORT int soundio_outstream_begin_write(struct SoundIoOutStream *outstream,
struct SoundIoChannelArea **areas, int *frame_count);
/// Commits the write that you began with ::soundio_outstream_begin_write.
/// You must call this function only from the SoundIoOutStream::write_callback thread context.
/// This function might return #SoundIoErrorUnderflow but don't count on it.
///
/// Possible errors:
/// * #SoundIoErrorStreaming
/// * #SoundIoErrorUnderflow - an underflow caused this call to fail. You might
/// also get a SoundIoOutStream::underflow_callback, and you might not get
/// this error code when an underflow occurs. Unlike #SoundIoErrorStreaming,
/// the outstream is still in a valid state and streaming can continue.
SOUNDIO_EXPORT int soundio_outstream_end_write(struct SoundIoOutStream *outstream);
/// Clears the output stream buffer.
/// You must call this function only from the SoundIoOutStream::write_callback thread context.
/// Possible errors:
///
/// * #SoundIoErrorStreaming
SOUNDIO_EXPORT int soundio_outstream_clear_buffer(struct SoundIoOutStream *outstream);
/// If the underyling device supports pausing, this pauses the stream and
@ -848,6 +925,11 @@ SOUNDIO_EXPORT int soundio_outstream_clear_buffer(struct SoundIoOutStream *outst
/// You must call this function only from the SoundIoOutStream::write_callback thread context.
/// Pausing when already paused or unpausing when already unpaused has no
/// effect and always returns SoundIoErrorNone.
///
/// Possible errors:
/// * #SoundIoErrorBackendDisconnected
/// * #SoundIoErrorStreaming
/// * #SoundIoErrorIncompatibleDevice - device does not support pausing/unpausing
SOUNDIO_EXPORT int soundio_outstream_pause(struct SoundIoOutStream *outstream, bool pause);
@ -855,7 +937,9 @@ SOUNDIO_EXPORT int soundio_outstream_pause(struct SoundIoOutStream *outstream, b
// Input Streams
/// Allocates memory and sets defaults. Next you should fill out the struct fields
/// and then call ::soundio_instream_open.
/// and then call ::soundio_instream_open. Sets all fields to defaults.
/// Returns `NULL` if and only if memory could not be allocated.
/// See also ::soundio_instream_destroy
SOUNDIO_EXPORT struct SoundIoInStream *soundio_instream_create(struct SoundIoDevice *device);
/// You may not call this function from SoundIoInStream::read_callback.
SOUNDIO_EXPORT void soundio_instream_destroy(struct SoundIoInStream *instream);
@ -865,9 +949,28 @@ SOUNDIO_EXPORT void soundio_instream_destroy(struct SoundIoInStream *instream);
/// The next thing to do is call ::soundio_instream_start.
/// If this function returns an error, the instream is in an invalid state and
/// you must call ::soundio_instream_destroy on it.
///
/// Possible errors:
/// * #SoundIoErrorInvalid
/// * device aim is not #SoundIoDeviceAimInput
/// * format is not valid
/// * requested layout channel count > #SOUNDIO_MAX_CHANNELS
/// * #SoundIoErrorOpeningDevice
/// * #SoundIoErrorNoMem
/// * #SoundIoErrorBackendDisconnected
/// * #SoundIoErrorSystemResources
/// * #SoundIoErrorNoSuchClient
/// * #SoundIoErrorIncompatibleBackend
/// * #SoundIoErrorIncompatibleDevice
SOUNDIO_EXPORT int soundio_instream_open(struct SoundIoInStream *instream);
/// After you call this function, SoundIoInStream::read_callback will be called.
///
/// Possible errors:
/// * #SoundIoErrorBackendDisconnected
/// * #SoundIoErrorStreaming
/// * #SoundIoErrorOpeningDevice
/// * #SoundIoErrorSystemResources
SOUNDIO_EXPORT int soundio_instream_start(struct SoundIoInStream *instream);
/// Call this function when you are ready to begin reading from the device
@ -881,7 +984,7 @@ SOUNDIO_EXPORT int soundio_instream_start(struct SoundIoInStream *instream);
/// returns the number of frames you can actually read. The returned value
/// will always be less than or equal to the provided value. If the provided
/// value is less than `frame_count_min` from SoundIoInStream::read_callback this function
/// returns with SoundIoErrorInvalid.
/// returns with #SoundIoErrorInvalid.
/// It is your responsibility to call this function no more and no fewer than the
/// correct number of times according to the `frame_count_min` and
/// `frame_count_max` criteria from SoundIoInStream::read_callback.
@ -891,6 +994,14 @@ SOUNDIO_EXPORT int soundio_instream_start(struct SoundIoInStream *instream);
/// and move the read index forward. ::soundio_instream_end_read should not be
/// called if the buffer is empty (`frame_count` == 0), but it should be called
/// if there is a hole.
///
/// Possible errors:
/// * #SoundIoErrorInvalid
/// * `*frame_count` < `frame_count_min` or `*frame_count` > `frame_count_max`
/// * #SoundIoErrorStreaming
/// * #SoundIoErrorIncompatibleDevice - in rare cases it might just now
/// be discovered that the device uses non-byte-aligned access, in which
/// case this error code is returned.
SOUNDIO_EXPORT int soundio_instream_begin_read(struct SoundIoInStream *instream,
struct SoundIoChannelArea **areas, int *frame_count);
/// This will drop all of the frames from when you called
@ -898,6 +1009,9 @@ SOUNDIO_EXPORT int soundio_instream_begin_read(struct SoundIoInStream *instream,
/// You must call this function only from the SoundIoInStream::read_callback thread context.
/// You must call this function only after a successful call to
/// ::soundio_instream_begin_read.
///
/// Possible errors:
/// * #SoundIoErrorStreaming
SOUNDIO_EXPORT int soundio_instream_end_read(struct SoundIoInStream *instream);
/// If the underyling device supports pausing, this pauses the stream and
@ -906,6 +1020,11 @@ SOUNDIO_EXPORT int soundio_instream_end_read(struct SoundIoInStream *instream);
/// You must call this function only from the SoundIoInStream::read_callback thread context.
/// Pausing when already paused or unpausing when already unpaused has no
/// effect and always returns SoundIoErrorNone.
///
/// Possible errors:
/// * #SoundIoErrorBackendDisconnected
/// * #SoundIoErrorStreaming
/// * #SoundIoErrorIncompatibleDevice - device does not support pausing/unpausing
SOUNDIO_EXPORT int soundio_instream_pause(struct SoundIoInStream *instream, bool pause);
@ -913,6 +1032,7 @@ SOUNDIO_EXPORT int soundio_instream_pause(struct SoundIoInStream *instream, bool
struct SoundIoRingBuffer;
/// `requested_capacity` in bytes.
/// See also ::soundio_ring_buffer_destroy
/// Returns `NULL` if and only if memory could not be allocated.
SOUNDIO_EXPORT struct SoundIoRingBuffer *soundio_ring_buffer_create(struct SoundIo *soundio, int requested_capacity);
SOUNDIO_EXPORT void soundio_ring_buffer_destroy(struct SoundIoRingBuffer *ring_buffer);

View file

@ -207,7 +207,8 @@ static int outstream_begin_write_dummy(SoundIoPrivate *si,
SoundIoOutStream *outstream = &os->pub;
SoundIoOutStreamDummy *osd = &os->backend_data.dummy;
assert(*frame_count <= osd->frames_left);
if (*frame_count > osd->frames_left)
return SoundIoErrorInvalid;
char *write_ptr = soundio_ring_buffer_write_ptr(&osd->ring_buffer);
for (int ch = 0; ch < outstream->layout.channel_count; ch += 1) {

View file

@ -667,8 +667,6 @@ static int outstream_open_pa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os) {
SoundIoOutStreamPulseAudio *ospa = &os->backend_data.pulseaudio;
SoundIoOutStream *outstream = &os->pub;
if (outstream->layout.channel_count > SOUNDIO_MAX_CHANNELS)
return SoundIoErrorInvalid;
if ((unsigned)outstream->layout.channel_count > PA_CHANNELS_MAX)
return SoundIoErrorIncompatibleBackend;
@ -882,8 +880,6 @@ static int instream_open_pa(SoundIoPrivate *si, SoundIoInStreamPrivate *is) {
SoundIoInStreamPulseAudio *ispa = &is->backend_data.pulseaudio;
SoundIoInStream *instream = &is->pub;
if (instream->layout.channel_count > SOUNDIO_MAX_CHANNELS)
return SoundIoErrorInvalid;
if ((unsigned)instream->layout.channel_count > PA_CHANNELS_MAX)
return SoundIoErrorIncompatibleBackend;
if (!instream->name)

View file

@ -245,7 +245,6 @@ void soundio_disconnect(struct SoundIo *soundio) {
soundio->current_backend = SoundIoBackendNone;
soundio_destroy_devices_info(si->safe_devices_info);
si->safe_devices_info = nullptr;
si->destroy = nullptr;
@ -424,7 +423,8 @@ int soundio_outstream_begin_write(struct SoundIoOutStream *outstream,
SoundIo *soundio = outstream->device->soundio;
SoundIoPrivate *si = (SoundIoPrivate *)soundio;
SoundIoOutStreamPrivate *os = (SoundIoOutStreamPrivate *)outstream;
assert(*frame_count > 0);
if (*frame_count <= 0)
return SoundIoErrorInvalid;
return si->outstream_begin_write(si, os, areas, frame_count);
}
@ -468,6 +468,9 @@ int soundio_outstream_open(struct SoundIoOutStream *outstream) {
if (device->probe_error)
return device->probe_error;
if (outstream->layout.channel_count > SOUNDIO_MAX_CHANNELS)
return SoundIoErrorInvalid;
if (outstream->format == SoundIoFormatInvalid) {
outstream->format = soundio_device_supports_format(device, SoundIoFormatFloat32NE) ?
SoundIoFormatFloat32NE : device->formats[0];
@ -522,6 +525,13 @@ int soundio_outstream_pause(struct SoundIoOutStream *outstream, bool pause) {
return si->outstream_pause(si, os, pause);
}
int soundio_outstream_clear_buffer(struct SoundIoOutStream *outstream) {
SoundIo *soundio = outstream->device->soundio;
SoundIoPrivate *si = (SoundIoPrivate *)soundio;
SoundIoOutStreamPrivate *os = (SoundIoOutStreamPrivate *)outstream;
return si->outstream_clear_buffer(si, os);
}
static void default_instream_error_callback(struct SoundIoInStream *is, int err) {
soundio_panic("libsoundio: %s", soundio_strerror(err));
}
@ -551,6 +561,9 @@ int soundio_instream_open(struct SoundIoInStream *instream) {
if (instream->format <= SoundIoFormatInvalid)
return SoundIoErrorInvalid;
if (instream->layout.channel_count > SOUNDIO_MAX_CHANNELS)
return SoundIoErrorInvalid;
if (device->probe_error)
return device->probe_error;