This commit is contained in:
Andrew Kelley 2015-09-24 08:01:00 -07:00
parent be6e654098
commit b62daa25a2
13 changed files with 613 additions and 935 deletions

View file

@ -1,3 +1,10 @@
### Version 2.0.0 (UNRELEASED)
* Default sample rate is now 44100 instead of 48000.
* `SoundIoOutStream` and `SoundIoInStream` no longer exist. Instead, there is
`SoundIoStream` and a stream can be an output stream, an input stream, or
both.
### Version 1.0.1 (2015-09-11)
* libsoundio no longer depends on or links against libm.

View file

@ -140,7 +140,6 @@ set(LIBSOUNDIO_SOURCES
"${CMAKE_SOURCE_DIR}/src/os.cpp"
"${CMAKE_SOURCE_DIR}/src/dummy.cpp"
"${CMAKE_SOURCE_DIR}/src/channel_layout.cpp"
"${CMAKE_SOURCE_DIR}/src/ring_buffer.cpp"
)
set(CONFIGURE_OUT_FILE "${CMAKE_BINARY_DIR}/config.h")

View file

@ -13,39 +13,6 @@
#include <string.h>
#include <math.h>
struct SoundIoRingBuffer *ring_buffer = NULL;
static enum SoundIoFormat prioritized_formats[] = {
SoundIoFormatFloat32NE,
SoundIoFormatFloat32FE,
SoundIoFormatS32NE,
SoundIoFormatS32FE,
SoundIoFormatS24NE,
SoundIoFormatS24FE,
SoundIoFormatS16NE,
SoundIoFormatS16FE,
SoundIoFormatFloat64NE,
SoundIoFormatFloat64FE,
SoundIoFormatU32NE,
SoundIoFormatU32FE,
SoundIoFormatU24NE,
SoundIoFormatU24FE,
SoundIoFormatU16NE,
SoundIoFormatU16FE,
SoundIoFormatS8,
SoundIoFormatU8,
SoundIoFormatInvalid,
};
static int prioritized_sample_rates[] = {
48000,
44100,
96000,
24000,
0,
};
__attribute__ ((cold))
__attribute__ ((noreturn))
__attribute__ ((format (printf, 1, 2)))
@ -62,28 +29,46 @@ static int min_int(int a, int b) {
return (a < b) ? a : b;
}
static void read_callback(struct SoundIoInStream *instream, int frame_count_min, int frame_count_max) {
struct SoundIoChannelArea *areas;
static void error_callback(struct SoundIoStream *stream, int err) {
panic("stream aborted: %s", soundio_strerror(err));
}
static void audio_callback(struct SoundIoStream *stream, int frame_count_min, int frame_count_max) {
struct SoundIoChannelArea *in_areas;
struct SoundIoChannelArea *out_areas;
int err;
char *write_ptr = soundio_ring_buffer_write_ptr(ring_buffer);
int free_bytes = soundio_ring_buffer_free_count(ring_buffer);
int free_count = free_bytes / instream->bytes_per_frame;
if (frame_count_min > free_count)
panic("ring buffer overflow");
int frames_left = frame_count_max;
int write_frames = min_int(free_count, frame_count_max);
int frames_left = write_frames;
int read_frame_count = -1;
for (;;) {
int frame_count = frames_left;
while (frames_left > 0) {
int write_frame_count = frames_left;
if ((err = soundio_instream_begin_read(instream, &areas, &frame_count)))
if ((err = soundio_stream_begin_write(stream, &out_areas, &write_frame_count)))
panic("begin read error: %s", soundio_strerror(err));
if (!frame_count)
if (!write_frame_count)
break;
for (int frame = 0; frame < write_frame_count; frame += 1, read_frame_count -= 1) {
if (read_frame_count == 0) {
if ((err = soundio_stream_end_read(stream)))
panic("begin read error: %s", soundio_strerror(err));
}
if (read_frame_count <= 0) {
if ((err = soundio_stream_begin_read(stream, &in_areas, &read_frame_count)))
panic("begin read error: %s", soundio_strerror(err));
}
for (int ch = 0; ch < stream->output_layout.channel_count; ch += 1) {
memcpy(write_ptr, areas[ch].ptr, stream->bytes_per_sample);
out_areas[ch].ptr += out_areas[ch].step;
write_ptr += instream->bytes_per_sample;
}
}
if (!areas) {
// Due to an overflow there is a hole. Fill the ring buffer with
// silence for the size of the hole.
@ -106,9 +91,6 @@ static void read_callback(struct SoundIoInStream *instream, int frame_count_min,
if (frames_left <= 0)
break;
}
int advance_bytes = write_frames * instream->bytes_per_frame;
soundio_ring_buffer_advance_write_ptr(ring_buffer, advance_bytes);
}
static void write_callback(struct SoundIoOutStream *outstream, int frame_count_min, int frame_count_max) {
@ -167,19 +149,22 @@ static void write_callback(struct SoundIoOutStream *outstream, int frame_count_m
soundio_ring_buffer_advance_read_ptr(ring_buffer, read_count * outstream->bytes_per_frame);
}
static void underflow_callback(struct SoundIoOutStream *outstream) {
static void underflow_callback(struct SoundIoStream *stream) {
static int count = 0;
fprintf(stderr, "underflow %d\n", ++count);
}
static void overflow_callback(struct SoundIoStream *stream) {
static int count = 0;
fprintf(stderr, "overflow %d\n", ++count);
}
static int usage(char *exe) {
fprintf(stderr, "Usage: %s [options]\n"
"Options:\n"
" [--backend dummy|alsa|pulseaudio|jack|coreaudio|wasapi]\n"
" [--in-device id]\n"
" [--in-raw]\n"
" [--out-device id]\n"
" [--out-raw]\n"
" [--device id]\n"
" [--raw]\n"
" [--latency seconds]\n"
, exe);
return 1;
@ -188,20 +173,16 @@ static int usage(char *exe) {
int main(int argc, char **argv) {
char *exe = argv[0];
enum SoundIoBackend backend = SoundIoBackendNone;
char *in_device_id = NULL;
char *out_device_id = NULL;
bool in_raw = false;
bool out_raw = false;
char *device_id = NULL;
bool raw = false;
double microphone_latency = 0.2; // seconds
for (int i = 1; i < argc; i += 1) {
char *arg = argv[i];
if (arg[0] == '-' && arg[1] == '-') {
if (strcmp(arg, "--in-raw") == 0) {
in_raw = true;
} else if (strcmp(arg, "--out-raw") == 0) {
out_raw = true;
if (strcmp(arg, "--raw") == 0) {
raw = true;
} else if (++i >= argc) {
return usage(exe);
} else if (strcmp(arg, "--backend") == 0) {
@ -221,10 +202,8 @@ int main(int argc, char **argv) {
fprintf(stderr, "Invalid backend: %s\n", argv[i]);
return 1;
}
} else if (strcmp(arg, "--in-device") == 0) {
in_device_id = argv[i];
} else if (strcmp(arg, "--out-device") == 0) {
out_device_id = argv[i];
} else if (strcmp(arg, "--device") == 0) {
device_id = argv[i];
} else if (strcmp(arg, "--latency") == 0) {
microphone_latency = atof(argv[i]);
} else {
@ -253,89 +232,52 @@ int main(int argc, char **argv) {
if (default_in_device_index < 0)
panic("no output device found");
int in_device_index = default_in_device_index;
if (in_device_id) {
struct SoundIoDevice *in_device;
if (device_id) {
bool found = false;
for (int i = 0; i < soundio_input_device_count(soundio); i += 1) {
struct SoundIoDevice *device = soundio_get_input_device(soundio, i);
if (device->is_raw == in_raw && strcmp(device->id, in_device_id) == 0) {
in_device_index = i;
if (device->is_raw == raw && strcmp(device->id, device_id) == 0) {
in_device = device;
found = true;
soundio_device_unref(device);
break;
}
soundio_device_unref(device);
}
if (!found)
panic("invalid input device id: %s", in_device_id);
panic("invalid input device id: %s", device_id);
} else {
in_device = soundio_get_input_device(soundio, default_in_device_index);
}
int out_device_index = default_out_device_index;
if (out_device_id) {
bool found = false;
for (int i = 0; i < soundio_output_device_count(soundio); i += 1) {
struct SoundIoDevice *device = soundio_get_output_device(soundio, i);
if (device->is_raw == out_raw && strcmp(device->id, out_device_id) == 0) {
out_device_index = i;
found = true;
soundio_device_unref(device);
break;
}
soundio_device_unref(device);
struct SoundIoDevice *out_device;
bool found = false;
for (int i = 0; i < soundio_output_device_count(soundio); i += 1) {
struct SoundIoDevice *device = soundio_get_output_device(soundio, i);
if (device->is_raw == in_device->is_raw && strcmp(device->id, in_device->id) == 0) {
out_device = device;
found = true;
break;
}
if (!found)
panic("invalid output device id: %s", out_device_id);
soundio_device_unref(device);
}
if (!found)
panic("invalid output device id: %s", in_device->id);
struct SoundIoDevice *out_device = soundio_get_output_device(soundio, out_device_index);
if (!out_device)
panic("could not get output device: out of memory");
struct SoundIoDevice *in_device = soundio_get_input_device(soundio, in_device_index);
if (!in_device)
panic("could not get input device: out of memory");
fprintf(stderr, "Input device: %s\n", in_device->name);
fprintf(stderr, "Output device: %s\n", out_device->name);
soundio_device_sort_channel_layouts(out_device);
const struct SoundIoChannelLayout *layout = soundio_best_matching_channel_layout(
out_device->layouts, out_device->layout_count,
in_device->layouts, in_device->layout_count);
if (!layout)
panic("channel layouts not compatible");
int *sample_rate;
for (sample_rate = prioritized_sample_rates; *sample_rate; sample_rate += 1) {
if (soundio_device_supports_sample_rate(in_device, *sample_rate) &&
soundio_device_supports_sample_rate(out_device, *sample_rate))
{
break;
}
}
if (!*sample_rate)
panic("incompatible sample rates");
enum SoundIoFormat *fmt;
for (fmt = prioritized_formats; *fmt != SoundIoFormatInvalid; fmt += 1) {
if (soundio_device_supports_format(in_device, *fmt) &&
soundio_device_supports_format(out_device, *fmt))
{
break;
}
}
if (*fmt == SoundIoFormatInvalid)
panic("incompatible sample formats");
struct SoundIoInStream *instream = soundio_instream_create(in_device);
if (!instream)
struct SoundIoStream *stream = soundio_stream_create(in_device, out_device);
if (!stream)
panic("out of memory");
instream->format = *fmt;
instream->sample_rate = *sample_rate;
instream->layout = *layout;
instream->software_latency = microphone_latency;
instream->read_callback = read_callback;
stream->software_latency = microphone_latency;
stream->audio_callback = audio_callback;
stream->underflow_callback = underflow_callback;
stream->overflow_callback = overflow_callback;
stream->error_callback = error_callback;
if ((err = soundio_instream_open(instream))) {
fprintf(stderr, "unable to open input stream: %s", soundio_strerror(err));
@ -345,12 +287,7 @@ int main(int argc, char **argv) {
struct SoundIoOutStream *outstream = soundio_outstream_create(out_device);
if (!outstream)
panic("out of memory");
outstream->format = *fmt;
outstream->sample_rate = *sample_rate;
outstream->layout = *layout;
outstream->software_latency = microphone_latency;
outstream->write_callback = write_callback;
outstream->underflow_callback = underflow_callback;
if ((err = soundio_outstream_open(outstream))) {
fprintf(stderr, "unable to open output stream: %s", soundio_strerror(err));

View file

@ -228,6 +228,27 @@ enum SoundIoDeviceAim {
/// For your convenience, Native Endian and Foreign Endian constants are defined
/// which point to the respective SoundIoFormat values.
///
/// When choosing a default sample format, libsoundio tries the formats in
/// this order:
/// * #SoundIoFormatFloat32NE
/// * #SoundIoFormatFloat32FE
/// * #SoundIoFormatS32NE
/// * #SoundIoFormatS32FE
/// * #SoundIoFormatU32NE
/// * #SoundIoFormatU32FE
/// * #SoundIoFormatFloat64NE
/// * #SoundIoFormatFloat64FE
/// * #SoundIoFormatS24NE
/// * #SoundIoFormatS24FE
/// * #SoundIoFormatU24NE
/// * #SoundIoFormatU24FE
/// * #SoundIoFormatS16NE
/// * #SoundIoFormatS16FE
/// * #SoundIoFormatU16NE
/// * #SoundIoFormatU16FE
/// * #SoundIoFormatS8
/// * #SoundIoFormatU8
enum SoundIoFormat {
SoundIoFormatInvalid,
SoundIoFormatS8, ///< Signed 8 bit
@ -293,7 +314,7 @@ enum SoundIoFormat {
#error unknown byte order
#endif
#define SOUNDIO_MAX_CHANNELS 24
#define SOUNDIO_MAX_CHANNELS 16
/// The size of this struct is OK to use.
struct SoundIoChannelLayout {
const char *name;
@ -388,6 +409,8 @@ struct SoundIoDevice {
/// supports raw mode, there may be up to four devices with the same id:
/// one for each value of SoundIoDevice::is_raw and one for each value of
/// SoundIoDevice::aim.
/// To open a duplex stream, input_device and output_device must have
/// the same id.
char *id;
/// User-friendly UTF-8 encoded text to describe the device.
char *name;
@ -485,60 +508,99 @@ struct SoundIoDevice {
int probe_error;
};
/// This struct represents an output stream, an input stream, or a duplex
/// stream. A duplex stream is both an output stream and an input stream.
/// The size of this struct is not part of the API or ABI.
struct SoundIoOutStream {
/// Populated automatically when you call ::soundio_outstream_create.
struct SoundIoDevice *device;
struct SoundIoStream {
/// Populated automatically when you call ::soundio_stream_create.
/// NULL if this stream is not an input stream.
struct SoundIoDevice *input_device;
/// Defaults to #SoundIoFormatFloat32NE, followed by the first one
/// Populated automatically when you call ::soundio_stream_create.
/// NULL if this stream is not an output stream.
struct SoundIoDevice *output_device;
/// For input streams.
/// Defaults to the best format supported by input_device.
/// See SoundIoFormat for the priority listing.
enum SoundIoFormat input_format;
/// For output streams.
/// Defaults to the best format supported by output_device.
/// See SoundIoFormat for the priority listing.
enum SoundIoFormat output_format;
/// For input streams.
/// Defaults to the layout with the largest number of channels that is
/// supported.
enum SoundIoFormat format;
struct SoundIoChannelLayout input_layout;
/// For output streams.
/// Defaults to the layout with the largest number of channels that is
/// supported.
struct SoundIoChannelLayout output_layout;
/// Sample rate is the number of frames per second.
/// Defaults to 48000 (and then clamped into range).
/// Defaults to 44100 and then clamped into the closest supported value
/// by input_device if applicable and output_device if applicable.
int sample_rate;
/// Defaults to Stereo, if available, followed by the first layout
/// supported.
struct SoundIoChannelLayout layout;
/// Ignoring hardware latency, this is the number of seconds it takes for
/// the last sample in a full buffer to be played.
/// After you call ::soundio_outstream_open, this value is replaced with the
/// For output streams, ignoring hardware latency, this is the number of
/// seconds it takes for the last sample in a full buffer to be played.
///
/// For input streams, ignoring hardware latency, this is the number of
/// seconds it takes for a captured sample to become available for reading.
///
/// After you call ::soundio_stream_open, this value is replaced with the
/// actual software latency, as near to this value as possible.
/// On systems that support clearing the buffer, this defaults to a large
/// latency, potentially upwards of 2 seconds, with the understanding that
/// you will call ::soundio_outstream_clear_buffer when you want to reduce
/// the latency to 0. On systems that do not support clearing the buffer,
/// this defaults to a reasonable lower latency value.
///
/// On backends with high latencies (such as 2 seconds), `frame_count_min`
/// will be 0, meaning you don't have to fill the entire buffer. In this
/// case, the large buffer is there if you want it; you only have to fill
/// as much as you want. On backends like JACK, `frame_count_min` will be
/// equal to `frame_count_max` and if you don't fill that many frames, you
/// will get glitches.
/// For output streams, on systems that support clearing the buffer, this
/// defaults to a large latency, potentially upwards of 2 seconds, with the
/// understanding that you will call ::soundio_stream_clear_buffer when you
/// want to reduce the latency to 0. On systems that do not support clearing
/// the buffer, this defaults to a reasonable lower latency value.
///
/// If the device has unknown software latency min and max values, you may
/// still set this, but you might not get the value you requested.
/// For PulseAudio, if you set this value to non-default, it sets
/// `PA_STREAM_ADJUST_LATENCY` and is the value used for `maxlength` and
/// `tlength`.
/// For output streams, on backends with high latencies (such as 2 seconds),
/// `frame_count_min` will be 0, meaning you don't have to fill the entire
/// buffer. In this case, the large buffer is there if you want it; you only
/// have to fill as much as you want. On backends like JACK,
///`frame_count_min` will be equal to `frame_count_max` and if you don't
/// fill that many frames, you will get glitches.
///
/// If the device has unknown software latency min and
/// max values, you may still set this, but you might not get the value you
/// requested.
///
/// For PulseAudio output streams, if you set this value to non-default, it
/// sets `PA_STREAM_ADJUST_LATENCY` and is the value used for `maxlength`
/// and `tlength`.
///
/// For input streams, a higher value means less CPU usage. Defaults to a
/// large value, potentially upwards of 2 seconds. For PulseAudio, if you
/// set this value to non-default, it sets `PA_STREAM_ADJUST_LATENCY` and
/// is the value used for `fragsize`.
///
/// For JACK, this value is always equal to
/// SoundIoDevice::software_latency_current of the device.
/// SoundIoDevice::software_latency_current.
double software_latency;
/// Defaults to NULL. Put whatever you want here.
void *userdata;
/// In this callback, you call ::soundio_outstream_begin_write and
/// ::soundio_outstream_end_write as many times as necessary to write
/// In this callback, for output streams, call ::soundio_stream_begin_write
/// and ::soundio_stream_end_write as many times as necessary to write
/// at minimum `frame_count_min` frames and at maximum `frame_count_max`
/// frames. `frame_count_max` will always be greater than 0. Note that you
/// should write as many frames as you can; `frame_count_min` might be 0 and
/// you can still get a buffer underflow if you always write
/// `frame_count_min` frames.
///
/// In this callback, for input streams, call ::soundio_stream_begin_read
/// and ::soundio_stream_end_read as many times as necessary to read at
/// minimum `frame_count_min` frames and at maximum `frame_count_max`
/// frames. If you return from read_callback without having read
/// `frame_count_min`, the frames will be dropped. `frame_count_max` is how
/// many frames are available to read.
///
/// For Dummy, ALSA, and PulseAudio, `frame_count_min` will be 0. For JACK
/// and CoreAudio `frame_count_min` will be equal to `frame_count_max`.
///
@ -547,106 +609,31 @@ struct SoundIoOutStream {
/// for a long time. This includes all I/O functions (disk, TTY, network),
/// malloc, free, printf, pthread_mutex_lock, sleep, wait, poll, select,
/// pthread_join, pthread_cond_wait, etc.
void (*write_callback)(struct SoundIoOutStream *,
void (*audio_callback)(struct SoundIoStream *,
int frame_count_min, int frame_count_max);
/// This callback is for output streams.
/// This optional callback happens when the sound device runs out of
/// buffered audio data to play. After this occurs, the outstream waits
/// buffered audio data to play. After this occurs, the stream waits
/// until the buffer is full to resume playback.
/// This is called from the SoundIoOutStream::write_callback thread context.
void (*underflow_callback)(struct SoundIoOutStream *);
/// Optional callback. `err` is always SoundIoErrorStreaming.
/// SoundIoErrorStreaming is an unrecoverable error. The stream is in an
/// invalid state and must be destroyed.
/// If you do not supply error_callback, the default callback will print
/// a message to stderr and then call `abort`.
/// This is called from the SoundIoOutStream::write_callback thread context.
void (*error_callback)(struct SoundIoOutStream *, int err);
/// Optional: Name of the stream. Defaults to "SoundIoOutStream"
/// PulseAudio uses this for the stream name.
/// JACK uses this for the client name of the client that connects when you
/// open the stream.
/// WASAPI uses this for the session display name.
/// Must not contain a colon (":").
const char *name;
/// Optional: Hint that this output stream is nonterminal. This is used by
/// JACK and it means that the output stream data originates from an input
/// stream. Defaults to `false`.
bool non_terminal_hint;
/// computed automatically when you call ::soundio_outstream_open
int bytes_per_frame;
/// computed automatically when you call ::soundio_outstream_open
int bytes_per_sample;
/// If setting the channel layout fails for some reason, this field is set
/// to an error code. Possible error codes are:
/// * #SoundIoErrorIncompatibleDevice
int layout_error;
};
/// The size of this struct is not part of the API or ABI.
struct SoundIoInStream {
/// Populated automatically when you call ::soundio_outstream_create.
struct SoundIoDevice *device;
/// Defaults to #SoundIoFormatFloat32NE, followed by the first one
/// supported.
enum SoundIoFormat format;
/// Sample rate is the number of frames per second.
/// Defaults to max(sample_rate_min, min(sample_rate_max, 48000))
int sample_rate;
/// Defaults to Stereo, if available, followed by the first layout
/// supported.
struct SoundIoChannelLayout layout;
/// Ignoring hardware latency, this is the number of seconds it takes for a
/// captured sample to become available for reading.
/// After you call ::soundio_instream_open, this value is replaced with the
/// actual software latency, as near to this value as possible.
/// A higher value means less CPU usage. Defaults to a large value,
/// potentially upwards of 2 seconds.
/// If the device has unknown software latency min and max values, you may
/// still set this, but you might not get the value you requested.
/// For PulseAudio, if you set this value to non-default, it sets
/// `PA_STREAM_ADJUST_LATENCY` and is the value used for `fragsize`.
/// For JACK, this value is always equal to
/// SoundIoDevice::software_latency_current
double software_latency;
/// Defaults to NULL. Put whatever you want here.
void *userdata;
/// In this function call ::soundio_instream_begin_read and
/// ::soundio_instream_end_read as many times as necessary to read at
/// minimum `frame_count_min` frames and at maximum `frame_count_max`
/// frames. If you return from read_callback without having read
/// `frame_count_min`, the frames will be dropped. `frame_count_max` is how
/// many frames are available to read.
///
/// The code in the supplied function must be suitable for real-time
/// execution. That means that it cannot call functions that might block
/// for a long time. This includes all I/O functions (disk, TTY, network),
/// malloc, free, printf, pthread_mutex_lock, sleep, wait, poll, select,
/// pthread_join, pthread_cond_wait, etc.
void (*read_callback)(struct SoundIoInStream *, int frame_count_min, int frame_count_max);
/// This is called from the SoundIoStream::write_callback thread context.
void (*underflow_callback)(struct SoundIoStream *);
/// This function is for input streams.
/// This optional callback happens when the sound device buffer is full,
/// yet there is more captured audio to put in it.
/// This is never fired for PulseAudio.
/// This is called from the SoundIoInStream::read_callback thread context.
void (*overflow_callback)(struct SoundIoInStream *);
/// Optional callback. `err` is always SoundIoErrorStreaming.
/// SoundIoErrorStreaming is an unrecoverable error. The stream is in an
/// This is called from the SoundIoStream::read_callback thread context.
void (*overflow_callback)(struct SoundIoStream *);
/// This callback is for both input and output streams.
/// Optional callback. `err` is always #SoundIoErrorStreaming.
/// #SoundIoErrorStreaming is an unrecoverable error. The stream is in an
/// invalid state and must be destroyed.
/// If you do not supply `error_callback`, the default callback will print
/// a message to stderr and then abort().
/// This is called from the SoundIoInStream::read_callback thread context.
void (*error_callback)(struct SoundIoInStream *, int err);
/// a message to stderr and then call `abort`.
/// This is called from the SoundIoStream::write_callback or
/// SoundIoStream::read_callback thread context.
void (*error_callback)(struct SoundIoStream *, int err);
/// Optional: Name of the stream. Defaults to "SoundIoInStream";
/// Optional: Name of the stream. Defaults to "SoundIoStream"
/// PulseAudio uses this for the stream name.
/// JACK uses this for the client name of the client that connects when you
/// open the stream.
@ -655,21 +642,40 @@ struct SoundIoInStream {
const char *name;
/// Optional: Hint that this input stream is nonterminal. This is used by
/// JACK and it means that the data received by the stream will be
/// passed on or made available to another stream. Defaults to `false`.
bool non_terminal_hint;
/// JACK. It means that the data received by the stream will be
/// passed on or made available to another stream.
/// Defaults to `false`.
bool input_non_terminal_hint;
/// computed automatically when you call ::soundio_instream_open
int bytes_per_frame;
/// computed automatically when you call ::soundio_instream_open
int bytes_per_sample;
/// Optional: Hint that this output stream is nonterminal. This is used by
/// JACK. It means that the output stream data originates from an input
/// stream.
/// Defaults to `false`.
bool output_non_terminal_hint;
/// If setting the channel layout fails for some reason, this field is set
/// to an error code. Possible error codes are: #SoundIoErrorIncompatibleDevice
int layout_error;
/// computed automatically when you call ::soundio_stream_open
int input_bytes_per_frame;
/// computed automatically when you call ::soundio_stream_open
int output_bytes_per_frame;
/// computed automatically when you call ::soundio_stream_open
int input_bytes_per_sample;
/// computed automatically when you call ::soundio_stream_open
int output_bytes_per_sample;
/// If setting the input channel layout fails for some reason, this field
/// is set to an error code. Possible error codes are:
/// * #SoundIoErrorIncompatibleDevice
int input_layout_error;
/// If setting the output channel layout fails for some reason, this field
/// is set to an error code. Possible error codes are:
/// * #SoundIoErrorIncompatibleDevice
int output_layout_error;
};
// Main Context
/// Create a SoundIo context. You may create multiple instances of this to
/// connect to multiple backends. Sets all fields to defaults.
@ -760,12 +766,10 @@ SOUNDIO_EXPORT void soundio_wakeup(struct SoundIo *soundio);
/// SoundIo::on_devices_change callback.
///
/// This can be called from any thread context except for
/// SoundIoOutStream::write_callback and SoundIoInStream::read_callback
/// SoundIoStream::write_callback and SoundIoStream::read_callback
SOUNDIO_EXPORT void soundio_force_device_scan(struct SoundIo *soundio);
// Channel Layouts
/// Returns whether the channel count field and each channel id matches in
/// the supplied channel layouts.
SOUNDIO_EXPORT bool soundio_channel_layout_equal(
@ -808,8 +812,6 @@ SOUNDIO_EXPORT const struct SoundIoChannelLayout *soundio_best_matching_channel_
SOUNDIO_EXPORT void soundio_sort_channel_layouts(struct SoundIoChannelLayout *layouts, int layout_count);
// Sample Formats
/// Returns -1 on invalid format.
SOUNDIO_EXPORT int soundio_get_bytes_per_sample(enum SoundIoFormat format);
@ -831,15 +833,6 @@ SOUNDIO_EXPORT const char * soundio_format_string(enum SoundIoFormat format);
// Devices
/// When you call ::soundio_flush_events, a snapshot of all device state is
/// saved and these functions merely access the snapshot data. When you want
/// to check for new devices, call ::soundio_flush_events. Or you can call
/// ::soundio_wait_events to block until devices change. If an error occurs
/// scanning devices in a background thread, SoundIo::on_backend_disconnect is called
/// with the error code.
/// Get the number of input devices.
/// Returns -1 if you never called ::soundio_flush_events.
SOUNDIO_EXPORT int soundio_input_device_count(struct SoundIo *soundio);
@ -905,63 +898,76 @@ 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. Sets all fields to defaults.
/// Allocates memory and sets defaults. Next you should complete the struct fields
/// and then call ::soundio_stream_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);
/// See also ::soundio_stream_destroy
SOUNDIO_EXPORT struct SoundIoStream *soundio_stream_create(
struct SoundIoDevice *input_device, struct SoundIoDevice *output_device);
/// You may not call this function from the SoundIoStream::write_callback or
/// SoundIoStream::read_callback thread context.
SOUNDIO_EXPORT void soundio_stream_destroy(struct SoundIoStream *stream);
/// After you call this function, SoundIoOutStream:software_latency is set to the correct
/// After you call this function, SoundIoStream:software_latency is set to the correct
/// value.
/// 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.
/// The next thing to do is call ::soundio_stream_start.
/// If this function returns an error, the stream is in an invalid state and
/// you must call ::soundio_stream_destroy on it.
///
/// If you call this function without specifying SoundIoStream::layout,
/// ::soundio_sort_channel_layouts may be called on SoundIoDevice::layouts of
/// SoundIoStream::input_device or SoundIoStream::output_device in order to
/// select a default channel layout.
///
/// Possible errors:
/// * #SoundIoErrorInvalid
/// * SoundIoDevice::aim is not #SoundIoDeviceAimOutput
/// * SoundIoOutStream::format is not valid
/// * SoundIoOutStream::channel_count is greater than #SOUNDIO_MAX_CHANNELS
/// * SoundIoStream::input_device is not an input device.
/// * SoundIoStream::output_device is not an output device.
/// * SoundIoStream::format is not valid
/// * SoundIoStream::channel_count is greater than #SOUNDIO_MAX_CHANNELS
/// * SoundIoStream::input_device and SoundIoStream::output_device do not
/// have equal ids.
/// * #SoundIoErrorNoMem
/// * #SoundIoErrorOpeningDevice
/// * #SoundIoErrorBackendDisconnected
/// * #SoundIoErrorSystemResources
/// * #SoundIoErrorNoSuchClient - when JACK returns `JackNoSuchClient`
/// * #SoundIoErrorOpeningDevice
/// * #SoundIoErrorIncompatibleBackend - SoundIoOutStream::channel_count is
/// * #SoundIoErrorIncompatibleBackend - SoundIoStream::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);
/// compatible with the chosen devices.
SOUNDIO_EXPORT int soundio_stream_open(struct SoundIoStream *stream);
/// After you call this function, SoundIoOutStream::write_callback will be called.
/// After you this function is called, SoundIoStream::write_callback will be
/// called for output streams and SoundIoStream::read_callback will be called
/// for input streams.
///
/// Possible errors:
/// * #SoundIoErrorStreaming
/// * #SoundIoErrorNoMem
/// * #SoundIoErrorSystemResources
/// * #SoundIoErrorBackendDisconnected
SOUNDIO_EXPORT int soundio_outstream_start(struct SoundIoOutStream *outstream);
/// * #SoundIoErrorOpeningDevice
SOUNDIO_EXPORT int soundio_stream_start(struct SoundIoStream *stream);
/// This function is for output streams.
/// Call this function when you are ready to begin writing to the device buffer.
/// * `outstream` - (in) The output stream you want to write to.
/// * `stream` - (in) The output stream you want to write to.
/// * `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
/// ::soundio_outstream_end_write. The value returned will always be less
/// ::soundio_stream_end_write. The value returned will always be less
/// than or equal to the value provided.
/// It is your responsibility to call this function exactly as many times as
/// necessary to meet the `frame_count_min` and `frame_count_max` criteria from
/// SoundIoOutStream::write_callback.
/// You must call this function only from the SoundIoOutStream::write_callback thread context.
/// SoundIoStream::write_callback.
/// You must call this function only from the SoundIoStream::write_callback thread context.
/// After calling this function, write data to `areas` and then call
/// ::soundio_outstream_end_write.
/// If this function returns an error, do not call ::soundio_outstream_end_write.
/// ::soundio_stream_end_write.
/// If this function returns an error, do not call ::soundio_stream_end_write.
///
/// Possible errors:
/// * #SoundIoErrorInvalid
@ -970,116 +976,31 @@ SOUNDIO_EXPORT int soundio_outstream_start(struct SoundIoOutStream *outstream);
/// * 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
/// also get a SoundIoStream::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.
/// the stream 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,
SOUNDIO_EXPORT int soundio_stream_begin_write(struct SoundIoStream *stream,
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 is for output streams.
/// Commits the write that you began with ::soundio_stream_begin_write.
/// You must call this function only from the SoundIoStream::write_callback thread context.
///
/// Possible errors:
/// * #SoundIoErrorStreaming
/// * #SoundIoErrorUnderflow - an underflow caused this call to fail. You might
/// also get a SoundIoOutStream::underflow_callback, and you might not get
/// also get a SoundIoStream::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.
/// This function can be called from any thread.
/// This function can be called regardless of whether the outstream is paused
/// or not.
/// Some backends do not support clearing the buffer. On these backends this
/// function will return SoundIoErrorIncompatibleBackend.
/// Some devices do not support clearing the buffer. On these devices this
/// function might return SoundIoErrorIncompatibleDevice.
/// Possible errors:
///
/// * #SoundIoErrorStreaming
/// * #SoundIoErrorIncompatibleBackend
/// * #SoundIoErrorIncompatibleDevice
SOUNDIO_EXPORT int soundio_outstream_clear_buffer(struct SoundIoOutStream *outstream);
/// If the underlying device supports pausing, this pauses the stream.
/// SoundIoOutStream::write_callback may be called a few more times if the
/// buffer is not full.
/// Pausing might put the hardware into a low power state which is ideal if your
/// software is silent for some time.
/// This function may be called from any thread.
/// 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. This error code might not be returned even if the
/// device does not support pausing/unpausing.
/// * #SoundIoErrorInvalid - outstream not opened and started
SOUNDIO_EXPORT int soundio_outstream_pause(struct SoundIoOutStream *outstream, bool pause);
/// Obtain the total number of seconds that the next frame written after the
/// last frame written with ::soundio_outstream_end_write will take to become
/// audible. This includes both software and hardware latency. In other words,
/// if you call this function directly after calling ::soundio_outstream_end_write,
/// this gives you the number of seconds that the next frame written will take
/// to become audible.
///
/// This function must be called only from within SoundIoOutStream::write_callback.
///
/// Possible errors:
/// * #SoundIoErrorStreaming
SOUNDIO_EXPORT int soundio_outstream_get_latency(struct SoundIoOutStream *outstream,
double *out_latency);
// Input Streams
/// Allocates memory and sets defaults. Next you should fill out the struct fields
/// 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);
/// After you call this function, SoundIoInStream::software_latency is set to the correct
/// value.
/// 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);
/// the stream is still in a valid state and streaming can continue.
SOUNDIO_EXPORT int soundio_stream_end_write(struct SoundIoStream *stream);
/// This function is for input streams.
/// Call this function when you are ready to begin reading from the device
/// buffer.
/// * `instream` - (in) The input stream you want to read from.
/// * `stream` - (in) The input stream you want to read from.
/// * `areas` - (out) The memory addresses you can read data from. It is OK
/// to modify the pointers if that helps you iterate. There might be a "hole"
/// in the buffer. To indicate this, `areas` will be `NULL` and `frame_count`
@ -1087,15 +1008,15 @@ SOUNDIO_EXPORT int soundio_instream_start(struct SoundIoInStream *instream);
/// * `frame_count` - (in/out) - Provide the number of frames you want to read;
/// 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
/// value is less than `frame_count_min` from SoundIoStream::read_callback this function
/// 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.
/// You must call this function only from the SoundIoInStream::read_callback thread context.
/// `frame_count_max` criteria from SoundIoStream::read_callback.
/// You must call this function only from the SoundIoStream::read_callback thread context.
/// After calling this function, read data from `areas` and then use
/// ::soundio_instream_end_read` to actually remove the data from the buffer
/// and move the read index forward. ::soundio_instream_end_read should not be
/// ::soundio_stream_end_read` to actually remove the data from the buffer
/// and move the read index forward. ::soundio_stream_end_read should not be
/// called if the buffer is empty (`frame_count` == 0), but it should be called
/// if there is a hole.
///
@ -1106,77 +1027,77 @@ SOUNDIO_EXPORT int soundio_instream_start(struct SoundIoInStream *instream);
/// * #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,
SOUNDIO_EXPORT int soundio_stream_begin_read(struct SoundIoStream *stream,
struct SoundIoChannelArea **areas, int *frame_count);
/// This function is for input streams.
/// This will drop all of the frames from when you called
/// ::soundio_instream_begin_read.
/// You must call this function only from the SoundIoInStream::read_callback thread context.
/// ::soundio_stream_begin_read.
/// You must call this function only from the SoundIoStream::read_callback thread context.
/// You must call this function only after a successful call to
/// ::soundio_instream_begin_read.
/// ::soundio_stream_begin_read.
///
/// Possible errors:
/// * #SoundIoErrorStreaming
SOUNDIO_EXPORT int soundio_instream_end_read(struct SoundIoInStream *instream);
SOUNDIO_EXPORT int soundio_stream_end_read(struct SoundIoStream *stream);
/// If the underyling device supports pausing, this pauses the stream and
/// prevents SoundIoInStream::read_callback from being called. Otherwise this returns
/// #SoundIoErrorIncompatibleDevice.
/// Clears the stream hardware buffer.
/// This function can be called from any thread.
/// This function can be called regardless of whether the stream is paused.
/// Some backends do not support clearing the buffer. On these backends this
/// function will return SoundIoErrorIncompatibleBackend.
/// Some devices do not support clearing the buffer. On these devices this
/// function might return SoundIoErrorIncompatibleDevice.
///
/// Possible errors:
///
/// * #SoundIoErrorStreaming
/// * #SoundIoErrorIncompatibleBackend
/// * #SoundIoErrorIncompatibleDevice
SOUNDIO_EXPORT int soundio_stream_clear_buffer(struct SoundIoStream *stream);
/// If the underlying device supports pausing, this pauses the stream.
/// SoundIoStream::write_callback may be called a few more times if the
/// buffer is not full.
/// SoundIoStream::read_callback will stop being called if this function
/// succeeds, however it may be called once more after the function returns
/// and/or may be in the middle of being called when this function returns.
/// Pausing might put the hardware into a low power state which is ideal if your
/// software is silent for some time.
/// This function may be called from any thread.
/// Pausing when already paused or unpausing when already unpaused has no
/// effect and always returns SoundIoErrorNone.
/// 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);
/// * #SoundIoErrorIncompatibleDevice - device does not support
/// pausing/unpausing. This error code might not be returned even if the
/// device does not support pausing/unpausing.
/// * #SoundIoErrorInvalid - stream not opened and started
SOUNDIO_EXPORT int soundio_stream_pause(struct SoundIoStream *stream, bool pause);
/// Obtain the number of seconds that the next frame of sound being
/// captured will take to arrive in the buffer, plus the amount of time that is
/// represented in the buffer. This includes both software and hardware latency.
/// For output streams, obtain the total number of seconds that the next frame
/// written after the last frame written with ::soundio_stream_end_write will
/// take to become audible. This includes both software and hardware latency.
/// In other words, if you call this function directly after calling
/// ::soundio_stream_end_write, this gives you the number of seconds that the
/// next frame written will take to become audible.
///
/// This function must be called only from within SoundIoInStream::read_callback.
/// For input streams, obtain the number of seconds that the next frame of
/// sound being captured will take to arrive in the buffer, plus the amount of
/// time that is represented in the buffer. This includes both software and
/// hardware latency.
///
/// For output streams, this function must be called only from within
/// SoundIoStream::write_callback.
///
/// For input streams, this function must be called only from within
/// SoundIoStream::read_callback.
///
/// Possible errors:
/// * #SoundIoErrorStreaming
SOUNDIO_EXPORT int soundio_instream_get_latency(struct SoundIoInStream *instream,
SOUNDIO_EXPORT int soundio_stream_get_latency(struct SoundIoStream *stream,
double *out_latency);
/// A ring buffer is a single-reader single-writer lock-free fixed-size queue.
/// libsoundio ring buffers use memory mapping techniques to enable a
/// contiguous buffer when reading or writing across the boundary of the ring
/// buffer's capacity.
struct SoundIoRingBuffer;
/// `requested_capacity` in bytes.
/// Returns `NULL` if and only if memory could not be allocated.
/// Use ::soundio_ring_buffer_capacity to get the actual capacity, which might
/// be greater for alignment purposes.
/// See also ::soundio_ring_buffer_destroy
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);
/// When you create a ring buffer, capacity might be more than the requested
/// capacity for alignment purposes. This function returns the actual capacity.
SOUNDIO_EXPORT int soundio_ring_buffer_capacity(struct SoundIoRingBuffer *ring_buffer);
/// Do not write more than capacity.
SOUNDIO_EXPORT char *soundio_ring_buffer_write_ptr(struct SoundIoRingBuffer *ring_buffer);
/// `count` in bytes.
SOUNDIO_EXPORT void soundio_ring_buffer_advance_write_ptr(struct SoundIoRingBuffer *ring_buffer, int count);
/// Do not read more than capacity.
SOUNDIO_EXPORT char *soundio_ring_buffer_read_ptr(struct SoundIoRingBuffer *ring_buffer);
/// `count` in bytes.
SOUNDIO_EXPORT void soundio_ring_buffer_advance_read_ptr(struct SoundIoRingBuffer *ring_buffer, int count);
/// Returns how many bytes of the buffer is used, ready for reading.
SOUNDIO_EXPORT int soundio_ring_buffer_fill_count(struct SoundIoRingBuffer *ring_buffer);
/// Returns how many bytes of the buffer is free, ready for writing.
SOUNDIO_EXPORT int soundio_ring_buffer_free_count(struct SoundIoRingBuffer *ring_buffer);
/// Must be called by the writer.
SOUNDIO_EXPORT void soundio_ring_buffer_clear(struct SoundIoRingBuffer *ring_buffer);
#endif

View file

@ -43,8 +43,9 @@ struct SoundIoAlsa {
bool emitted_shutdown_cb;
};
struct SoundIoOutStreamAlsa {
snd_pcm_t *handle;
struct SoundIoStreamAlsa {
snd_pcm_t *out_handle;
snd_pcm_t *in_handle;
snd_pcm_chmap_t *chmap;
int chmap_size;
snd_pcm_uframes_t offset;
@ -58,27 +59,11 @@ struct SoundIoOutStreamAlsa {
atomic_flag thread_exit_flag;
int period_size;
int write_frame_count;
bool is_paused;
atomic_flag clear_buffer_flag;
SoundIoChannelArea areas[SOUNDIO_MAX_CHANNELS];
};
struct SoundIoInStreamAlsa {
snd_pcm_t *handle;
snd_pcm_chmap_t *chmap;
int chmap_size;
snd_pcm_uframes_t offset;
snd_pcm_access_t access;
int sample_buffer_size;
char *sample_buffer;
int poll_fd_count;
struct pollfd *poll_fds;
SoundIoOsThread *thread;
atomic_flag thread_exit_flag;
int period_size;
int read_frame_count;
bool is_paused;
SoundIoChannelArea areas[SOUNDIO_MAX_CHANNELS];
atomic_flag clear_buffer_flag;
SoundIoChannelArea in_areas[SOUNDIO_MAX_CHANNELS];
SoundIoChannelArea out_areas[SOUNDIO_MAX_CHANNELS];
};
#endif

View file

@ -11,62 +11,62 @@
#include <stdio.h>
#include <string.h>
static void playback_thread_run(void *arg) {
SoundIoOutStreamPrivate *os = (SoundIoOutStreamPrivate *)arg;
SoundIoOutStream *outstream = &os->pub;
SoundIoOutStreamDummy *osd = &os->backend_data.dummy;
static void stream_thread_run(void *arg) {
SoundIoStreamPrivate *os = (SoundIoStreamPrivate *)arg;
SoundIoStream *stream = &os->pub;
SoundIoStreamDummy *sd = &os->backend_data.dummy;
int fill_bytes = soundio_ring_buffer_fill_count(&osd->ring_buffer);
int free_bytes = soundio_ring_buffer_capacity(&osd->ring_buffer) - fill_bytes;
int free_frames = free_bytes / outstream->bytes_per_frame;
osd->frames_left = free_frames;
if (free_frames > 0)
outstream->write_callback(outstream, 0, free_frames);
if (stream->output_device) {
int out_fill_frames = (sd->out_buffer_write_offset - sd->out_buffer_read_offset);
int out_free_frames = sd->buffer_frame_count - out_fill_frames;
sd->out_frames_left = out_free_frames;
if (out_free_frames > 0)
stream->write_callback(stream, 0, out_free_frames);
}
double start_time = soundio_os_get_time();
long frames_consumed = 0;
while (osd->abort_flag.test_and_set()) {
while (sd->abort_flag.test_and_set()) {
double now = soundio_os_get_time();
double time_passed = now - start_time;
double next_period = start_time +
ceil_dbl(time_passed / osd->period_duration) * osd->period_duration;
ceil_dbl(time_passed / sd->period_duration) * sd->period_duration;
double relative_time = next_period - now;
soundio_os_cond_timed_wait(osd->cond, nullptr, relative_time);
if (!osd->clear_buffer_flag.test_and_set()) {
soundio_ring_buffer_clear(&osd->ring_buffer);
int free_bytes = soundio_ring_buffer_capacity(&osd->ring_buffer);
int free_frames = free_bytes / outstream->bytes_per_frame;
osd->frames_left = free_frames;
if (free_frames > 0)
outstream->write_callback(outstream, 0, free_frames);
soundio_os_cond_timed_wait(sd->cond, nullptr, relative_time);
if (!sd->clear_buffer_flag.test_and_set()) {
sd->out_buffer_write_offset.store(0);
sd->out_buffer_read_offset.store(0);
sd->in_buffer_write_offset.store(0);
sd->in_buffer_read_offset.store(0);
sd->out_frames_left = sd->buffer_frame_count;
assert(sd->buffer_frame_count > 0);
stream->write_callback(stream, 0, sd->buffer_frame_count);
frames_consumed = 0;
start_time = soundio_os_get_time();
continue;
}
int fill_bytes = soundio_ring_buffer_fill_count(&osd->ring_buffer);
int fill_frames = fill_bytes / outstream->bytes_per_frame;
int free_bytes = soundio_ring_buffer_capacity(&osd->ring_buffer) - fill_bytes;
int free_frames = free_bytes / outstream->bytes_per_frame;
int out_fill_frames = (sd->out_buffer_write_offset - sd->out_buffer_read_offset);
int out_free_frames = sd->buffer_frame_count - out_fill_frames;
double total_time = soundio_os_get_time() - start_time;
long total_frames = total_time * outstream->sample_rate;
long total_frames = total_time * stream->sample_rate;
int frames_to_kill = total_frames - frames_consumed;
int read_count = min(frames_to_kill, fill_frames);
int byte_count = read_count * outstream->bytes_per_frame;
soundio_ring_buffer_advance_read_ptr(&osd->ring_buffer, byte_count);
int byte_count = read_count * stream->bytes_per_frame;
soundio_ring_buffer_advance_read_ptr(&sd->ring_buffer, byte_count);
frames_consumed += read_count;
if (frames_to_kill > fill_frames) {
outstream->underflow_callback(outstream);
osd->frames_left = free_frames;
stream->underflow_callback(stream);
sd->out_frames_left = free_frames;
if (free_frames > 0)
outstream->write_callback(outstream, 0, free_frames);
stream->write_callback(stream, 0, free_frames);
frames_consumed = 0;
start_time = soundio_os_get_time();
} else if (free_frames > 0) {
osd->frames_left = free_frames;
outstream->write_callback(outstream, 0, free_frames);
sd->out_frames_left = free_frames;
stream->write_callback(stream, 0, free_frames);
}
}
}
@ -205,7 +205,7 @@ static int outstream_pause_dummy(struct SoundIoPrivate *si, struct SoundIoOutStr
if (!osd->thread) {
osd->abort_flag.test_and_set();
int err;
if ((err = soundio_os_thread_create(playback_thread_run, os,
if ((err = soundio_os_thread_create(stream_thread_run, os,
soundio->emit_rtprio_warning, &osd->thread)))
{
return err;
@ -547,22 +547,16 @@ int soundio_dummy_init(SoundIoPrivate *si) {
si->wakeup = wakeup_dummy;
si->force_device_scan = force_device_scan_dummy;
si->outstream_open = outstream_open_dummy;
si->outstream_destroy = outstream_destroy_dummy;
si->outstream_start = outstream_start_dummy;
si->outstream_begin_write = outstream_begin_write_dummy;
si->outstream_end_write = outstream_end_write_dummy;
si->outstream_clear_buffer = outstream_clear_buffer_dummy;
si->outstream_pause = outstream_pause_dummy;
si->outstream_get_latency = outstream_get_latency_dummy;
si->instream_open = instream_open_dummy;
si->instream_destroy = instream_destroy_dummy;
si->instream_start = instream_start_dummy;
si->stream_open = stream_open_dummy;
si->stream_destroy = stream_destroy_dummy;
si->stream_start = stream_start_dummy;
si->stream_begin_write = stream_begin_write_dummy;
si->stream_end_write = stream_end_write_dummy;
si->instream_begin_read = instream_begin_read_dummy;
si->instream_end_read = instream_end_read_dummy;
si->instream_pause = instream_pause_dummy;
si->instream_get_latency = instream_get_latency_dummy;
si->stream_clear_buffer = stream_clear_buffer_dummy;
si->stream_pause = stream_pause_dummy;
si->stream_get_latency = stream_get_latency_dummy;
return 0;
}

View file

@ -11,7 +11,6 @@
#include "soundio_private.h"
#include "os.h"
#include "atomics.hpp"
#include "ring_buffer.hpp"
int soundio_dummy_init(struct SoundIoPrivate *si);
@ -23,30 +22,28 @@ struct SoundIoDummy {
struct SoundIoDeviceDummy { };
struct SoundIoOutStreamDummy {
struct SoundIoStreamDummy {
struct SoundIoOsThread *thread;
struct SoundIoOsCond *cond;
atomic_flag abort_flag;
double period_duration;
int buffer_frame_count;
int frames_left;
int out_frames_left;
int write_frame_count;
struct SoundIoRingBuffer ring_buffer;
atomic_long out_buffer_write_offset;
atomic_long out_buffer_read_offset;
int in_frames_left;
int read_frame_count;
atomic_long in_buffer_write_offset;
atomic_long in_buffer_read_offset;
double playback_start_time;
atomic_flag clear_buffer_flag;
SoundIoChannelArea areas[SOUNDIO_MAX_CHANNELS];
};
struct SoundIoInStreamDummy {
struct SoundIoOsThread *thread;
struct SoundIoOsCond *cond;
atomic_flag abort_flag;
double period_duration;
int frames_left;
int read_frame_count;
int buffer_frame_count;
struct SoundIoRingBuffer ring_buffer;
SoundIoChannelArea areas[SOUNDIO_MAX_CHANNELS];
SoundIoChannelArea in_areas[SOUNDIO_MAX_CHANNELS];
SoundIoChannelArea out_areas[SOUNDIO_MAX_CHANNELS];
};
#endif

View file

@ -45,30 +45,23 @@ struct SoundIoOutStreamJackPort {
int dest_port_name_len;
};
struct SoundIoOutStreamJack {
jack_client_t *client;
int period_size;
int frames_left;
bool is_paused;
double hardware_latency;
SoundIoOutStreamJackPort ports[SOUNDIO_MAX_CHANNELS];
SoundIoChannelArea areas[SOUNDIO_MAX_CHANNELS];
};
struct SoundIoInStreamJackPort {
jack_port_t *dest_port;
const char *source_port_name;
int source_port_name_len;
};
struct SoundIoInStreamJack {
struct SoundIoStreamJack {
jack_client_t *client;
int period_size;
int frames_left;
bool is_paused;
double hardware_latency;
SoundIoInStreamJackPort ports[SOUNDIO_MAX_CHANNELS];
SoundIoChannelArea areas[SOUNDIO_MAX_CHANNELS];
char *buf_ptrs[SOUNDIO_MAX_CHANNELS];
SoundIoOutStreamJackPort out_ports[SOUNDIO_MAX_CHANNELS];
SoundIoOutStreamJackPort in_ports[SOUNDIO_MAX_CHANNELS];
SoundIoChannelArea in_areas[SOUNDIO_MAX_CHANNELS];
SoundIoChannelArea out_areas[SOUNDIO_MAX_CHANNELS];
char *in_buf_ptrs[SOUNDIO_MAX_CHANNELS];
};
#endif

View file

@ -39,26 +39,24 @@ struct SoundIoPulseAudio {
pa_proplist *props;
};
struct SoundIoOutStreamPulseAudio {
struct SoundIoStreamPulseAudio {
pa_stream *stream;
atomic_bool stream_ready;
pa_buffer_attr buffer_attr;
atomic_bool out_stream_ready;
pa_buffer_attr out_buffer_attr;
char *write_ptr;
size_t write_byte_count;
atomic_flag clear_buffer_flag;
SoundIoChannelArea areas[SOUNDIO_MAX_CHANNELS];
};
struct SoundIoInStreamPulseAudio {
pa_stream *stream;
atomic_bool stream_ready;
pa_buffer_attr buffer_attr;
atomic_bool in_stream_ready;
pa_buffer_attr in_buffer_attr;
char *peek_buf;
size_t peek_buf_index;
size_t peek_buf_size;
int peek_buf_frames_left;
int read_frame_count;
SoundIoChannelArea areas[SOUNDIO_MAX_CHANNELS];
SoundIoChannelArea in_areas[SOUNDIO_MAX_CHANNELS];
SoundIoChannelArea out_areas[SOUNDIO_MAX_CHANNELS];
};
#endif

View file

@ -1,91 +0,0 @@
/*
* Copyright (c) 2015 Andrew Kelley
*
* This file is part of libsoundio, which is MIT licensed.
* See http://opensource.org/licenses/MIT
*/
#include "ring_buffer.hpp"
#include "soundio.hpp"
#include "util.hpp"
#include <stdlib.h>
struct SoundIoRingBuffer *soundio_ring_buffer_create(struct SoundIo *soundio, int requested_capacity) {
SoundIoRingBuffer *rb = allocate<SoundIoRingBuffer>(1);
assert(requested_capacity > 0);
if (!rb) {
soundio_ring_buffer_destroy(rb);
return nullptr;
}
if (soundio_ring_buffer_init(rb, requested_capacity)) {
soundio_ring_buffer_destroy(rb);
return nullptr;
}
return rb;
}
void soundio_ring_buffer_destroy(struct SoundIoRingBuffer *rb) {
if (!rb)
return;
soundio_ring_buffer_deinit(rb);
free(rb);
}
int soundio_ring_buffer_capacity(struct SoundIoRingBuffer *rb) {
return rb->capacity;
}
char *soundio_ring_buffer_write_ptr(struct SoundIoRingBuffer *rb) {
return rb->mem.address + (rb->write_offset % rb->capacity);
}
void soundio_ring_buffer_advance_write_ptr(struct SoundIoRingBuffer *rb, int count) {
rb->write_offset += count;
assert(soundio_ring_buffer_fill_count(rb) >= 0);
}
char *soundio_ring_buffer_read_ptr(struct SoundIoRingBuffer *rb) {
return rb->mem.address + (rb->read_offset % rb->capacity);
}
void soundio_ring_buffer_advance_read_ptr(struct SoundIoRingBuffer *rb, int count) {
rb->read_offset += count;
assert(soundio_ring_buffer_fill_count(rb) >= 0);
}
int soundio_ring_buffer_fill_count(struct SoundIoRingBuffer *rb) {
int count = rb->write_offset - rb->read_offset;
assert(count >= 0);
assert(count <= rb->capacity);
return count;
}
int soundio_ring_buffer_free_count(struct SoundIoRingBuffer *rb) {
return rb->capacity - soundio_ring_buffer_fill_count(rb);
}
void soundio_ring_buffer_clear(struct SoundIoRingBuffer *rb) {
return rb->write_offset.store(rb->read_offset.load());
}
int soundio_ring_buffer_init(struct SoundIoRingBuffer *rb, int requested_capacity) {
int err;
if ((err = soundio_os_init_mirrored_memory(&rb->mem, requested_capacity)))
return err;
rb->write_offset = 0;
rb->read_offset = 0;
rb->capacity = rb->mem.capacity;
return 0;
}
void soundio_ring_buffer_deinit(struct SoundIoRingBuffer *rb) {
soundio_os_deinit_mirrored_memory(&rb->mem);
}

View file

@ -1,24 +0,0 @@
/*
* Copyright (c) 2015 Andrew Kelley
*
* This file is part of libsoundio, which is MIT licensed.
* See http://opensource.org/licenses/MIT
*/
#ifndef SOUNDIO_RING_BUFFER_HPP
#define SOUNDIO_RING_BUFFER_HPP
#include "atomics.hpp"
#include "os.h"
struct SoundIoRingBuffer {
SoundIoOsMirroredMemory mem;
atomic_long write_offset;
atomic_long read_offset;
int capacity;
};
int soundio_ring_buffer_init(struct SoundIoRingBuffer *rb, int requested_capacity);
void soundio_ring_buffer_deinit(struct SoundIoRingBuffer *rb);
#endif

View file

@ -63,6 +63,27 @@ static int (*backend_init_fns[])(SoundIoPrivate *) = {
[SoundIoBackendDummy] = soundio_dummy_init,
};
static const SoundIoFormat prioritized_formats[] = {
SoundIoFormatFloat32NE,
SoundIoFormatFloat32FE,
SoundIoFormatS32NE,
SoundIoFormatS32FE,
SoundIoFormatU32NE,
SoundIoFormatU32FE,
SoundIoFormatFloat64NE,
SoundIoFormatFloat64FE,
SoundIoFormatS24NE,
SoundIoFormatS24FE,
SoundIoFormatU24NE,
SoundIoFormatU24FE,
SoundIoFormatS16NE,
SoundIoFormatS16FE,
SoundIoFormatU16NE,
SoundIoFormatU16FE,
SoundIoFormatS8,
SoundIoFormatU8,
};
const char *soundio_strerror(int error) {
switch ((enum SoundIoError)error) {
case SoundIoErrorNone: return "(no error)";
@ -254,22 +275,16 @@ void soundio_disconnect(struct SoundIo *soundio) {
si->wakeup = nullptr;
si->force_device_scan = nullptr;
si->outstream_open = nullptr;
si->outstream_destroy = nullptr;
si->outstream_start = nullptr;
si->outstream_begin_write = nullptr;
si->outstream_end_write = nullptr;
si->outstream_clear_buffer = nullptr;
si->outstream_pause = nullptr;
si->outstream_get_latency = nullptr;
si->instream_open = nullptr;
si->instream_destroy = nullptr;
si->instream_start = nullptr;
si->instream_begin_read = nullptr;
si->instream_end_read = nullptr;
si->instream_pause = nullptr;
si->instream_get_latency = nullptr;
si->stream_open = nullptr;
si->stream_destroy = nullptr;
si->stream_start = nullptr;
si->stream_begin_write = nullptr;
si->stream_end_write = nullptr;
si->stream_begin_read = nullptr;
si->stream_end_read = nullptr;
si->stream_clear_buffer = nullptr;
si->stream_pause = nullptr;
si->stream_get_latency = nullptr;
}
void soundio_flush_events(struct SoundIo *soundio) {
@ -428,238 +443,216 @@ void soundio_force_device_scan(struct SoundIo *soundio) {
si->force_device_scan(si);
}
int soundio_outstream_begin_write(struct SoundIoOutStream *outstream,
int soundio_stream_begin_write(struct SoundIoStream *stream,
SoundIoChannelArea **areas, int *frame_count)
{
SoundIo *soundio = outstream->device->soundio;
SoundIo *soundio = stream->output_device->soundio;
SoundIoPrivate *si = (SoundIoPrivate *)soundio;
SoundIoOutStreamPrivate *os = (SoundIoOutStreamPrivate *)outstream;
SoundIoStreamPrivate *st = (SoundIoStreamPrivate *)stream;
if (*frame_count <= 0)
return SoundIoErrorInvalid;
return si->outstream_begin_write(si, os, areas, frame_count);
return si->stream_begin_write(si, st, areas, frame_count);
}
int soundio_outstream_end_write(struct SoundIoOutStream *outstream) {
SoundIo *soundio = outstream->device->soundio;
int soundio_stream_end_write(struct SoundIoStream *stream) {
SoundIo *soundio = stream->output_device->soundio;
SoundIoPrivate *si = (SoundIoPrivate *)soundio;
SoundIoOutStreamPrivate *os = (SoundIoOutStreamPrivate *)outstream;
return si->outstream_end_write(si, os);
SoundIoStreamPrivate *st = (SoundIoStreamPrivate *)stream;
return si->stream_end_write(si, st);
}
static void default_outstream_error_callback(struct SoundIoOutStream *os, int err) {
static void default_stream_error_callback(struct SoundIoStream *s, int err) {
soundio_panic("libsoundio: %s", soundio_strerror(err));
}
static void default_underflow_callback(struct SoundIoOutStream *outstream) { }
static void default_underflow_callback(struct SoundIoStream *stream) { }
static void default_overflow_callback(struct SoundIoStream *stream) { }
struct SoundIoOutStream *soundio_outstream_create(struct SoundIoDevice *device) {
SoundIoOutStreamPrivate *os = allocate<SoundIoOutStreamPrivate>(1);
SoundIoOutStream *outstream = &os->pub;
if (!os)
return nullptr;
if (!device)
struct SoundIoStream *soundio_stream_create(
struct SoundIoDevice *input_device, struct SoundIoDevice *output_device)
{
SoundIoStreamPrivate *st = allocate<SoundIoStreamPrivate>(1);
SoundIoStream *stream = &st->pub;
if (!st)
return nullptr;
outstream->device = device;
soundio_device_ref(device);
if (!input_device && !output_device)
return nullptr;
outstream->error_callback = default_outstream_error_callback;
outstream->underflow_callback = default_underflow_callback;
stream->input_device = input_device;
stream->output_device = output_device;
return outstream;
soundio_device_ref(input_device);
soundio_device_ref(output_device);
stream->error_callback = default_stream_error_callback;
stream->underflow_callback = default_underflow_callback;
stream->overflow_callback = default_overflow_callback;
return stream;
}
int soundio_outstream_open(struct SoundIoOutStream *outstream) {
SoundIoDevice *device = outstream->device;
static enum SoundIoFormat get_default_stream_format(struct SoundIoDevice *device) {
for (int i = 0; i < array_length(prioritized_formats); i += 1) {
SoundIoFormat format = prioritized_formats[i];
if (soundio_device_supports_format(device, format))
return format;
}
return SoundIoFormatInvalid;
}
if (device->aim != SoundIoDeviceAimOutput)
return SoundIoErrorInvalid;
static int choose_default_stream_sample_rate(struct SoundIoStream *stream) {
static const int target_sample_rate = 44100;
if (stream->input_device && stream->output_device) {
int nearest_input = soundio_device_nearest_sample_rate(stream->input_device, target_sample_rate);
int nearest_output = soundio_device_nearest_sample_rate(stream->output_device, nearest_input);
if (device->probe_error)
return device->probe_error;
if (nearest_input == nearest_output) {
stream->sample_rate = nearest_input;
return 0;
}
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];
return SoundIoErrorIncompatibleDevice;
} else if (stream->input_device) {
return soundio_device_nearest_sample_rate(stream->input_device, target_sample_rate);
} else if (stream->output_device) {
return soundio_device_nearest_sample_rate(stream->output_device, target_sample_rate);
}
if (outstream->format <= SoundIoFormatInvalid)
return SoundIoErrorInvalid;
}
static SoundIo *stream_soundio(SoundIoStream *stream) {
return stream->input_device ? stream->input_device->soundio : stream->output_device->soundio;
}
int soundio_stream_open(struct SoundIoStream *stream) {
SoundIoDevice *input_device = stream->input_device;
SoundIoDevice *output_device = stream->output_device;
int err;
if (input_device && output_device && strcmp(input_device->id, output_device->id) != 0)
return SoundIoErrorInvalid;
if (!outstream->layout.channel_count) {
const SoundIoChannelLayout *stereo = soundio_channel_layout_get_builtin(SoundIoChannelLayoutIdStereo);
outstream->layout = soundio_device_supports_layout(device, stereo) ? *stereo : device->layouts[0];
if (input_device) {
if (input_device->aim != SoundIoDeviceAimInput)
return SoundIoErrorInvalid;
if (stream->input_layout.channel_count > SOUNDIO_MAX_CHANNELS)
return SoundIoErrorInvalid;
if (stream->input_format < SoundIoFormatInvalid)
return SoundIoErrorInvalid;
if (input_device->probe_error)
return input_device->probe_error;
if (stream->input_format == SoundIoFormatInvalid) {
stream->input_format = get_default_stream_format(input_device);
if (stream->input_format == SoundIoFormatInvalid)
return SoundIoErrorIncompatibleDevice;
}
if (!stream->input_layout.channel_count) {
soundio_sort_channel_layouts(stream->input_device->layouts, stream->input_device->layout_count);
stream->input_layout = stream->input_device->layouts[0];
}
}
if (!outstream->sample_rate)
outstream->sample_rate = soundio_device_nearest_sample_rate(device, 48000);
if (output_device) {
if (output_device->aim != SoundIoDeviceAimOutput)
return SoundIoErrorInvalid;
if (stream->output_layout.channel_count > SOUNDIO_MAX_CHANNELS)
return SoundIoErrorInvalid;
if (stream->output_format < SoundIoFormatInvalid)
return SoundIoErrorInvalid;
if (output_device->probe_error)
return output_device->probe_error;
if (stream->output_format == SoundIoFormatInvalid) {
stream->output_format = get_default_stream_format(output_device);
if (stream->output_format == SoundIoFormatInvalid)
return SoundIoErrorIncompatibleDevice;
}
if (!stream->output_layout.channel_count) {
soundio_sort_channel_layouts(stream->output_device->layouts, stream->output_device->layout_count);
stream->output_layout = stream->output_device->layouts[0];
}
}
SoundIoOutStreamPrivate *os = (SoundIoOutStreamPrivate *)outstream;
outstream->bytes_per_frame = soundio_get_bytes_per_frame(outstream->format, outstream->layout.channel_count);
outstream->bytes_per_sample = soundio_get_bytes_per_sample(outstream->format);
if (!stream->sample_rate) {
if ((err = choose_default_stream_sample_rate(stream))) {
return err;
}
}
SoundIo *soundio = device->soundio;
SoundIoStreamPrivate *st = (SoundIoStreamPrivate *)stream;
stream->input_bytes_per_frame = soundio_get_bytes_per_frame(stream->input_format,
stream->input_layout.channel_count);
stream->input_bytes_per_sample = soundio_get_bytes_per_sample(stream->input_format);
stream->output_bytes_per_frame = soundio_get_bytes_per_frame(stream->output_format,
stream->output_layout.channel_count);
stream->output_bytes_per_sample = soundio_get_bytes_per_sample(stream->output_format);
SoundIo *soundio = stream_soundio(stream);
SoundIoPrivate *si = (SoundIoPrivate *)soundio;
return si->outstream_open(si, os);
return si->stream_open(si, st);
}
void soundio_outstream_destroy(SoundIoOutStream *outstream) {
if (!outstream)
void soundio_stream_destroy(SoundIoStream *stream) {
if (!stream)
return;
SoundIoOutStreamPrivate *os = (SoundIoOutStreamPrivate *)outstream;
SoundIo *soundio = outstream->device->soundio;
SoundIoStreamPrivate *st = (SoundIoStreamPrivate *)stream;
SoundIo *soundio = stream->input_device ? stream->input_device->soundio : stream->output_device->soundio;
SoundIoPrivate *si = (SoundIoPrivate *)soundio;
if (si->outstream_destroy)
si->outstream_destroy(si, os);
if (si->stream_destroy)
si->stream_destroy(si, st);
soundio_device_unref(outstream->device);
free(os);
soundio_device_unref(stream->input_device);
soundio_device_unref(stream->output_device);
free(st);
}
int soundio_outstream_start(struct SoundIoOutStream *outstream) {
SoundIo *soundio = outstream->device->soundio;
int soundio_stream_start(struct SoundIoStream *stream) {
SoundIo *soundio = stream_soundio(stream);
SoundIoPrivate *si = (SoundIoPrivate *)soundio;
SoundIoOutStreamPrivate *os = (SoundIoOutStreamPrivate *)outstream;
return si->outstream_start(si, os);
SoundIoStreamPrivate *st = (SoundIoStreamPrivate *)stream;
return si->stream_start(si, st);
}
int soundio_outstream_pause(struct SoundIoOutStream *outstream, bool pause) {
SoundIo *soundio = outstream->device->soundio;
int soundio_stream_pause(struct SoundIoStream *stream, bool pause) {
SoundIo *soundio = stream_soundio(stream);
SoundIoPrivate *si = (SoundIoPrivate *)soundio;
SoundIoOutStreamPrivate *os = (SoundIoOutStreamPrivate *)outstream;
return si->outstream_pause(si, os, pause);
SoundIoStreamPrivate *st = (SoundIoStreamPrivate *)stream;
return si->stream_pause(si, st, pause);
}
int soundio_outstream_clear_buffer(struct SoundIoOutStream *outstream) {
SoundIo *soundio = outstream->device->soundio;
int soundio_stream_clear_buffer(struct SoundIoStream *stream) {
SoundIo *soundio = stream_soundio(stream);
SoundIoPrivate *si = (SoundIoPrivate *)soundio;
SoundIoOutStreamPrivate *os = (SoundIoOutStreamPrivate *)outstream;
return si->outstream_clear_buffer(si, os);
SoundIoStreamPrivate *st = (SoundIoStreamPrivate *)stream;
return si->stream_clear_buffer(si, st);
}
int soundio_outstream_get_latency(struct SoundIoOutStream *outstream, double *out_latency) {
SoundIo *soundio = outstream->device->soundio;
int soundio_stream_get_latency(struct SoundIoStream *stream, double *out_latency) {
SoundIo *soundio = stream_soundio(stream);
SoundIoPrivate *si = (SoundIoPrivate *)soundio;
SoundIoOutStreamPrivate *os = (SoundIoOutStreamPrivate *)outstream;
return si->outstream_get_latency(si, os, out_latency);
SoundIoStreamPrivate *st = (SoundIoStreamPrivate *)stream;
return si->stream_get_latency(si, st, out_latency);
}
static void default_instream_error_callback(struct SoundIoInStream *is, int err) {
soundio_panic("libsoundio: %s", soundio_strerror(err));
}
static void default_overflow_callback(struct SoundIoInStream *instream) { }
struct SoundIoInStream *soundio_instream_create(struct SoundIoDevice *device) {
SoundIoInStreamPrivate *is = allocate<SoundIoInStreamPrivate>(1);
SoundIoInStream *instream = &is->pub;
if (!is)
return nullptr;
if (!device)
return nullptr;
instream->device = device;
soundio_device_ref(device);
instream->error_callback = default_instream_error_callback;
instream->overflow_callback = default_overflow_callback;
return instream;
}
int soundio_instream_open(struct SoundIoInStream *instream) {
SoundIoDevice *device = instream->device;
if (device->aim != SoundIoDeviceAimInput)
return SoundIoErrorInvalid;
if (instream->format <= SoundIoFormatInvalid)
return SoundIoErrorInvalid;
if (instream->layout.channel_count > SOUNDIO_MAX_CHANNELS)
return SoundIoErrorInvalid;
if (device->probe_error)
return device->probe_error;
if (instream->format == SoundIoFormatInvalid) {
instream->format = soundio_device_supports_format(device, SoundIoFormatFloat32NE) ?
SoundIoFormatFloat32NE : device->formats[0];
}
if (!instream->layout.channel_count) {
const SoundIoChannelLayout *stereo = soundio_channel_layout_get_builtin(SoundIoChannelLayoutIdStereo);
instream->layout = soundio_device_supports_layout(device, stereo) ? *stereo : device->layouts[0];
}
if (!instream->sample_rate)
instream->sample_rate = soundio_device_nearest_sample_rate(device, 48000);
instream->bytes_per_frame = soundio_get_bytes_per_frame(instream->format, instream->layout.channel_count);
instream->bytes_per_sample = soundio_get_bytes_per_sample(instream->format);
SoundIo *soundio = device->soundio;
SoundIoPrivate *si = (SoundIoPrivate *)soundio;
SoundIoInStreamPrivate *is = (SoundIoInStreamPrivate *)instream;
return si->instream_open(si, is);
}
int soundio_instream_start(struct SoundIoInStream *instream) {
SoundIo *soundio = instream->device->soundio;
SoundIoPrivate *si = (SoundIoPrivate *)soundio;
SoundIoInStreamPrivate *is = (SoundIoInStreamPrivate *)instream;
return si->instream_start(si, is);
}
void soundio_instream_destroy(struct SoundIoInStream *instream) {
if (!instream)
return;
SoundIoInStreamPrivate *is = (SoundIoInStreamPrivate *)instream;
SoundIo *soundio = instream->device->soundio;
SoundIoPrivate *si = (SoundIoPrivate *)soundio;
if (si->instream_destroy)
si->instream_destroy(si, is);
soundio_device_unref(instream->device);
free(is);
}
int soundio_instream_pause(struct SoundIoInStream *instream, bool pause) {
SoundIo *soundio = instream->device->soundio;
SoundIoPrivate *si = (SoundIoPrivate *)soundio;
SoundIoInStreamPrivate *is = (SoundIoInStreamPrivate *)instream;
return si->instream_pause(si, is, pause);
}
int soundio_instream_begin_read(struct SoundIoInStream *instream,
int soundio_stream_begin_read(struct SoundIoStream *stream,
struct SoundIoChannelArea **areas, int *frame_count)
{
SoundIo *soundio = instream->device->soundio;
SoundIo *soundio = stream->input_device->soundio;
SoundIoPrivate *si = (SoundIoPrivate *)soundio;
SoundIoInStreamPrivate *is = (SoundIoInStreamPrivate *)instream;
return si->instream_begin_read(si, is, areas, frame_count);
SoundIoStreamPrivate *st = (SoundIoStreamPrivate *)stream;
return si->stream_begin_read(si, st, areas, frame_count);
}
int soundio_instream_end_read(struct SoundIoInStream *instream) {
SoundIo *soundio = instream->device->soundio;
int soundio_stream_end_read(struct SoundIoStream *stream) {
SoundIo *soundio = stream->input_device->soundio;
SoundIoPrivate *si = (SoundIoPrivate *)soundio;
SoundIoInStreamPrivate *is = (SoundIoInStreamPrivate *)instream;
return si->instream_end_read(si, is);
}
int soundio_instream_get_latency(struct SoundIoInStream *instream, double *out_latency) {
SoundIo *soundio = instream->device->soundio;
SoundIoPrivate *si = (SoundIoPrivate *)soundio;
SoundIoInStreamPrivate *is = (SoundIoInStreamPrivate *)instream;
return si->instream_get_latency(si, is, out_latency);
SoundIoStreamPrivate *st = (SoundIoStreamPrivate *)stream;
return si->stream_end_read(si, st);
}
void soundio_destroy_devices_info(SoundIoDevicesInfo *devices_info) {
@ -691,10 +684,10 @@ SoundIoBackend soundio_get_backend(struct SoundIo *soundio, int index) {
return available_backends[index];
}
static bool layout_contains(const SoundIoChannelLayout *available_layouts, int available_layouts_count,
static bool layout_contains(const SoundIoChannelLayout *available_layouts, int available_layout_count,
const SoundIoChannelLayout *target_layout)
{
for (int i = 0; i < available_layouts_count; i += 1) {
for (int i = 0; i < available_layout_count; i += 1) {
const SoundIoChannelLayout *available_layout = &available_layouts[i];
if (soundio_channel_layout_equal(target_layout, available_layout))
return true;
@ -703,12 +696,12 @@ static bool layout_contains(const SoundIoChannelLayout *available_layouts, int a
}
const struct SoundIoChannelLayout *soundio_best_matching_channel_layout(
const struct SoundIoChannelLayout *preferred_layouts, int preferred_layouts_count,
const struct SoundIoChannelLayout *available_layouts, int available_layouts_count)
const struct SoundIoChannelLayout *preferred_layouts, int preferred_layout_count,
const struct SoundIoChannelLayout *available_layouts, int available_layout_count)
{
for (int i = 0; i < preferred_layouts_count; i += 1) {
for (int i = 0; i < preferred_layout_count; i += 1) {
const SoundIoChannelLayout *preferred_layout = &preferred_layouts[i];
if (layout_contains(available_layouts, available_layouts_count, preferred_layout))
if (layout_contains(available_layouts, available_layout_count, preferred_layout))
return preferred_layout;
}
return nullptr;
@ -725,11 +718,11 @@ static int compare_layouts(const void *a, const void *b) {
return 0;
}
void soundio_sort_channel_layouts(struct SoundIoChannelLayout *layouts, int layouts_count) {
void soundio_sort_channel_layouts(struct SoundIoChannelLayout *layouts, int layout_count) {
if (!layouts)
return;
qsort(layouts, layouts_count, sizeof(SoundIoChannelLayout), compare_layouts);
qsort(layouts, layout_count, sizeof(SoundIoChannelLayout), compare_layouts);
}
void soundio_device_sort_channel_layouts(struct SoundIoDevice *device) {

View file

@ -71,42 +71,23 @@ union SoundIoDeviceBackendData {
SoundIoDeviceDummy dummy;
};
union SoundIoOutStreamBackendData {
union SoundIoStreamBackendData {
#ifdef SOUNDIO_HAVE_JACK
SoundIoOutStreamJack jack;
SoundIoStreamJack jack;
#endif
#ifdef SOUNDIO_HAVE_PULSEAUDIO
SoundIoOutStreamPulseAudio pulseaudio;
SoundIoStreamPulseAudio pulseaudio;
#endif
#ifdef SOUNDIO_HAVE_ALSA
SoundIoOutStreamAlsa alsa;
SoundIoStreamAlsa alsa;
#endif
#ifdef SOUNDIO_HAVE_COREAUDIO
SoundIoOutStreamCoreAudio coreaudio;
SoundIoStreamCoreAudio coreaudio;
#endif
#ifdef SOUNDIO_HAVE_WASAPI
SoundIoOutStreamWasapi wasapi;
SoundIoStreamWasapi wasapi;
#endif
SoundIoOutStreamDummy dummy;
};
union SoundIoInStreamBackendData {
#ifdef SOUNDIO_HAVE_JACK
SoundIoInStreamJack jack;
#endif
#ifdef SOUNDIO_HAVE_PULSEAUDIO
SoundIoInStreamPulseAudio pulseaudio;
#endif
#ifdef SOUNDIO_HAVE_ALSA
SoundIoInStreamAlsa alsa;
#endif
#ifdef SOUNDIO_HAVE_COREAUDIO
SoundIoInStreamCoreAudio coreaudio;
#endif
#ifdef SOUNDIO_HAVE_WASAPI
SoundIoInStreamWasapi wasapi;
#endif
SoundIoInStreamDummy dummy;
SoundIoStreamDummy dummy;
};
struct SoundIoDevicesInfo {
@ -117,14 +98,9 @@ struct SoundIoDevicesInfo {
int default_input_index;
};
struct SoundIoOutStreamPrivate {
SoundIoOutStream pub;
SoundIoOutStreamBackendData backend_data;
};
struct SoundIoInStreamPrivate {
SoundIoInStream pub;
SoundIoInStreamBackendData backend_data;
struct SoundIoStreamPrivate {
SoundIoStream pub;
SoundIoStreamBackendData backend_data;
};
struct SoundIoPrivate {
@ -139,25 +115,18 @@ struct SoundIoPrivate {
void (*wakeup)(struct SoundIoPrivate *);
void (*force_device_scan)(struct SoundIoPrivate *);
int (*outstream_open)(struct SoundIoPrivate *, struct SoundIoOutStreamPrivate *);
void (*outstream_destroy)(struct SoundIoPrivate *, struct SoundIoOutStreamPrivate *);
int (*outstream_start)(struct SoundIoPrivate *, struct SoundIoOutStreamPrivate *);
int (*outstream_begin_write)(struct SoundIoPrivate *, struct SoundIoOutStreamPrivate *,
int (*stream_open)(struct SoundIoPrivate *, struct SoundIoStreamPrivate *);
void (*stream_destroy)(struct SoundIoPrivate *, struct SoundIoStreamPrivate *);
int (*stream_start)(struct SoundIoPrivate *, struct SoundIoStreamPrivate *);
int (*stream_begin_write)(struct SoundIoPrivate *, struct SoundIoStreamPrivate *,
SoundIoChannelArea **out_areas, int *out_frame_count);
int (*outstream_end_write)(struct SoundIoPrivate *, struct SoundIoOutStreamPrivate *);
int (*outstream_clear_buffer)(struct SoundIoPrivate *, struct SoundIoOutStreamPrivate *);
int (*outstream_pause)(struct SoundIoPrivate *, struct SoundIoOutStreamPrivate *, bool pause);
int (*outstream_get_latency)(struct SoundIoPrivate *, struct SoundIoOutStreamPrivate *, double *out_latency);
int (*instream_open)(struct SoundIoPrivate *, struct SoundIoInStreamPrivate *);
void (*instream_destroy)(struct SoundIoPrivate *, struct SoundIoInStreamPrivate *);
int (*instream_start)(struct SoundIoPrivate *, struct SoundIoInStreamPrivate *);
int (*instream_begin_read)(struct SoundIoPrivate *, struct SoundIoInStreamPrivate *,
int (*stream_end_write)(struct SoundIoPrivate *, struct SoundIoStreamPrivate *);
int (*stream_begin_read)(struct SoundIoPrivate *, struct SoundIoStreamPrivate *,
SoundIoChannelArea **out_areas, int *out_frame_count);
int (*instream_end_read)(struct SoundIoPrivate *, struct SoundIoInStreamPrivate *);
int (*instream_pause)(struct SoundIoPrivate *, struct SoundIoInStreamPrivate *, bool pause);
int (*instream_get_latency)(struct SoundIoPrivate *, struct SoundIoInStreamPrivate *, double *out_latency);
int (*stream_end_read)(struct SoundIoPrivate *, struct SoundIoStreamPrivate *);
int (*stream_clear_buffer)(struct SoundIoPrivate *, struct SoundIoStreamPrivate *);
int (*stream_pause)(struct SoundIoPrivate *, struct SoundIoStreamPrivate *, bool pause);
int (*stream_get_latency)(struct SoundIoPrivate *, struct SoundIoStreamPrivate *, double *out_latency);
SoundIoBackendData backend_data;
};