mirror of
https://github.com/Ryujinx/libsoundio.git
synced 2024-12-22 17:15:35 +00:00
WIP
This commit is contained in:
parent
be6e654098
commit
b62daa25a2
|
@ -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.
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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) {
|
||||
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 == out_raw && strcmp(device->id, out_device_id) == 0) {
|
||||
out_device_index = i;
|
||||
if (device->is_raw == in_device->is_raw && strcmp(device->id, in_device->id) == 0) {
|
||||
out_device = device;
|
||||
found = true;
|
||||
soundio_device_unref(device);
|
||||
break;
|
||||
}
|
||||
soundio_device_unref(device);
|
||||
}
|
||||
if (!found)
|
||||
panic("invalid output device id: %s", out_device_id);
|
||||
}
|
||||
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));
|
||||
|
|
|
@ -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
|
||||
|
|
27
src/alsa.hpp
27
src/alsa.hpp
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
21
src/jack.hpp
21
src/jack.hpp
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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
|
393
src/soundio.cpp
393
src/soundio.cpp
|
@ -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;
|
||||
|
||||
if (device->aim != SoundIoDeviceAimOutput)
|
||||
return SoundIoErrorInvalid;
|
||||
|
||||
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];
|
||||
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 (outstream->format <= SoundIoFormatInvalid)
|
||||
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 (!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 (nearest_input == nearest_output) {
|
||||
stream->sample_rate = nearest_input;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!outstream->sample_rate)
|
||||
outstream->sample_rate = soundio_device_nearest_sample_rate(device, 48000);
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
return SoundIoErrorInvalid;
|
||||
}
|
||||
|
||||
SoundIo *soundio = device->soundio;
|
||||
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 (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 (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];
|
||||
}
|
||||
}
|
||||
|
||||
if (!stream->sample_rate) {
|
||||
if ((err = choose_default_stream_sample_rate(stream))) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue