mirror of
https://github.com/Ryujinx/libsoundio.git
synced 2024-12-22 18:25:35 +00:00
stream API update; expose multiple channel layouts
This commit is contained in:
parent
f0c8c68592
commit
5b3fd175f8
|
@ -27,7 +27,6 @@ static void print_channel_layout(const struct SoundIoChannelLayout *layout) {
|
|||
fprintf(stderr, ", %s", soundio_get_channel_name(layout->channels[i]));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void print_device(struct SoundIoDevice *device, bool is_default) {
|
||||
|
@ -39,9 +38,18 @@ static void print_device(struct SoundIoDevice *device, bool is_default) {
|
|||
if (device->probe_error) {
|
||||
fprintf(stderr, " probe error: %s\n", soundio_strerror(device->probe_error));
|
||||
} else {
|
||||
fprintf(stderr, " channel layout: ");
|
||||
print_channel_layout(&device->channel_layout);
|
||||
fprintf(stderr, "\n");
|
||||
fprintf(stderr, " channel layouts:\n");
|
||||
for (int i = 0; i < device->layout_count; i += 1) {
|
||||
fprintf(stderr, " ");
|
||||
print_channel_layout(&device->layouts[i]);
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
if (device->current_layout.channel_count > 0) {
|
||||
fprintf(stderr, " current layout: ");
|
||||
print_channel_layout(&device->current_layout);
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
|
||||
fprintf(stderr, " min sample rate: %d\n", device->sample_rate_min);
|
||||
fprintf(stderr, " max sample rate: %d\n", device->sample_rate_max);
|
||||
if (device->sample_rate_current)
|
||||
|
|
|
@ -25,18 +25,18 @@ static void panic(const char *format, ...) {
|
|||
abort();
|
||||
}
|
||||
|
||||
static void read_callback(struct SoundIoInStream *in_stream) {
|
||||
static void read_callback(struct SoundIoInStream *instream) {
|
||||
fprintf(stderr, "read_callback\n");
|
||||
}
|
||||
|
||||
static void write_callback(struct SoundIoOutStream *out_stream, int requested_frame_count) {
|
||||
static void write_callback(struct SoundIoOutStream *outstream, int requested_frame_count) {
|
||||
fprintf(stderr, "write_callback\n");
|
||||
}
|
||||
|
||||
static void underrun_callback(struct SoundIoOutStream *out_stream) {
|
||||
static void underrun_callback(struct SoundIoOutStream *outstream) {
|
||||
static int count = 0;
|
||||
fprintf(stderr, "underrun %d\n", count++);
|
||||
soundio_out_stream_fill_with_silence(out_stream);
|
||||
soundio_outstream_fill_with_silence(outstream);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
|
@ -67,30 +67,50 @@ int main(int argc, char **argv) {
|
|||
fprintf(stderr, "Input device: %s\n", in_device->description);
|
||||
fprintf(stderr, "Output device: %s\n", out_device->description);
|
||||
|
||||
if (!soundio_channel_layout_equal(&in_device->channel_layout, &out_device->channel_layout))
|
||||
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");
|
||||
|
||||
double latency = 0.1;
|
||||
struct SoundIoInStream *instream = soundio_instream_create(in_device);
|
||||
if (!instream)
|
||||
panic("out of memory");
|
||||
instream->format = SoundIoFormatFloat32NE; // TODO pick compatible ones
|
||||
instream->sample_rate = 48000; // TODO pick compatible ones
|
||||
instream->layout = *layout;
|
||||
instream->latency = 0.1;
|
||||
instream->read_callback = read_callback;
|
||||
|
||||
struct SoundIoInStream *in_stream;
|
||||
soundio_in_stream_create(in_device, SoundIoFormatFloat32NE, 48000, latency, NULL,
|
||||
read_callback, &in_stream);
|
||||
if ((err = soundio_instream_open(instream)))
|
||||
panic("unable to open input stream: %s", soundio_strerror(err));
|
||||
|
||||
struct SoundIoOutStream *out_stream;
|
||||
soundio_out_stream_create(out_device, SoundIoFormatFloat32NE, 48000, latency, NULL,
|
||||
write_callback, underrun_callback, &out_stream);
|
||||
struct SoundIoOutStream *outstream = soundio_outstream_create(out_device);
|
||||
if (!outstream)
|
||||
panic("out of memory");
|
||||
outstream->format = SoundIoFormatFloat32NE;
|
||||
outstream->sample_rate = 48000;
|
||||
outstream->layout = *layout;
|
||||
outstream->latency = 0.1;
|
||||
outstream->write_callback = write_callback;
|
||||
outstream->underrun_callback = underrun_callback;
|
||||
|
||||
if ((err = soundio_in_stream_start(in_stream)))
|
||||
if ((err = soundio_outstream_open(outstream)))
|
||||
panic("unable to open output stream: %s", soundio_strerror(err));
|
||||
|
||||
if ((err = soundio_instream_start(instream)))
|
||||
panic("unable to start input device: %s", soundio_strerror(err));
|
||||
|
||||
if ((err = soundio_out_stream_start(out_stream)))
|
||||
if ((err = soundio_outstream_start(outstream)))
|
||||
panic("unable to start output device: %s", soundio_strerror(err));
|
||||
|
||||
for (;;)
|
||||
soundio_wait_events(soundio);
|
||||
|
||||
soundio_out_stream_destroy(out_stream);
|
||||
soundio_in_stream_destroy(in_stream);
|
||||
soundio_outstream_destroy(outstream);
|
||||
soundio_instream_destroy(instream);
|
||||
soundio_device_unref(in_device);
|
||||
soundio_device_unref(out_device);
|
||||
soundio_destroy(soundio);
|
||||
|
|
|
@ -28,20 +28,20 @@ static void panic(const char *format, ...) {
|
|||
static const float PI = 3.1415926535f;
|
||||
static float seconds_offset = 0.0f;
|
||||
|
||||
static void write_callback(struct SoundIoOutStream *out_stream, int requested_frame_count) {
|
||||
static void write_callback(struct SoundIoOutStream *outstream, int requested_frame_count) {
|
||||
//device->bytes_per_frame;
|
||||
float float_sample_rate = out_stream->sample_rate;
|
||||
float float_sample_rate = outstream->sample_rate;
|
||||
float seconds_per_frame = 1.0f / float_sample_rate;
|
||||
|
||||
while (requested_frame_count > 0) {
|
||||
char *data;
|
||||
int frame_count = requested_frame_count;
|
||||
soundio_out_stream_begin_write(out_stream, &data, &frame_count);
|
||||
soundio_outstream_begin_write(outstream, &data, &frame_count);
|
||||
|
||||
// clear everything to 0
|
||||
memset(data, 0, frame_count * out_stream->bytes_per_frame);
|
||||
memset(data, 0, frame_count * outstream->bytes_per_frame);
|
||||
|
||||
const struct SoundIoChannelLayout *channel_layout = &out_stream->device->channel_layout;
|
||||
const struct SoundIoChannelLayout *layout = &outstream->layout;
|
||||
|
||||
float *ptr = (float *)data;
|
||||
|
||||
|
@ -50,14 +50,14 @@ static void write_callback(struct SoundIoOutStream *out_stream, int requested_fr
|
|||
float radians_per_second = pitch * 2.0f * PI;
|
||||
for (int frame = 0; frame < frame_count; frame += 1) {
|
||||
float sample = sinf((seconds_offset + frame * seconds_per_frame) * radians_per_second);
|
||||
for (int channel = 0; channel < channel_layout->channel_count; channel += 1) {
|
||||
for (int channel = 0; channel < layout->channel_count; channel += 1) {
|
||||
*ptr += sample;
|
||||
ptr += 1;
|
||||
}
|
||||
}
|
||||
seconds_offset += seconds_per_frame * frame_count;
|
||||
|
||||
soundio_out_stream_write(out_stream, data, frame_count);
|
||||
soundio_outstream_write(outstream, data, frame_count);
|
||||
requested_frame_count -= frame_count;
|
||||
}
|
||||
|
||||
|
@ -87,17 +87,24 @@ int main(int argc, char **argv) {
|
|||
|
||||
fprintf(stderr, "Output device: %s: %s\n", device->name, device->description);
|
||||
|
||||
struct SoundIoOutStream *out_stream;
|
||||
soundio_out_stream_create(device, SoundIoFormatFloat32NE, 48000,
|
||||
0.1, NULL, write_callback, underrun_callback, &out_stream);
|
||||
struct SoundIoOutStream *outstream = soundio_outstream_create(device);
|
||||
outstream->format = SoundIoFormatFloat32NE;
|
||||
outstream->sample_rate = 48000; // TODO let this be anything
|
||||
outstream->layout = device->layouts[0];
|
||||
outstream->latency = 0.1;
|
||||
outstream->write_callback = write_callback;
|
||||
outstream->underrun_callback = underrun_callback;
|
||||
|
||||
if ((err = soundio_out_stream_start(out_stream)))
|
||||
if ((err = soundio_outstream_open(outstream)))
|
||||
panic("unable to open device: %s", soundio_strerror(err));
|
||||
|
||||
if ((err = soundio_outstream_start(outstream)))
|
||||
panic("unable to start device: %s", soundio_strerror(err));
|
||||
|
||||
for (;;)
|
||||
soundio_wait_events(soundio);
|
||||
|
||||
soundio_out_stream_destroy(out_stream);
|
||||
soundio_outstream_destroy(outstream);
|
||||
soundio_device_unref(device);
|
||||
soundio_destroy(soundio);
|
||||
return 0;
|
||||
|
|
459
src/alsa.cpp
459
src/alsa.cpp
|
@ -30,6 +30,10 @@ struct SoundIoAlsa {
|
|||
struct SoundIoDevicesInfo *ready_devices_info;
|
||||
};
|
||||
|
||||
struct SoundIoOutStreamAlsa {
|
||||
|
||||
};
|
||||
|
||||
static void wakeup_device_poll(SoundIoAlsa *sia) {
|
||||
ssize_t amt = write(sia->notify_pipe_fd[1], "a", 1);
|
||||
if (amt == -1) {
|
||||
|
@ -41,8 +45,8 @@ static void wakeup_device_poll(SoundIoAlsa *sia) {
|
|||
}
|
||||
}
|
||||
|
||||
static void destroy_alsa(SoundIo *soundio) {
|
||||
SoundIoAlsa *sia = (SoundIoAlsa *)soundio->backend_data;
|
||||
static void destroy_alsa(SoundIoPrivate *si) {
|
||||
SoundIoAlsa *sia = (SoundIoAlsa *)si->backend_data;
|
||||
if (!sia)
|
||||
return;
|
||||
|
||||
|
@ -67,7 +71,7 @@ static void destroy_alsa(SoundIo *soundio) {
|
|||
close(sia->notify_fd);
|
||||
|
||||
destroy(sia);
|
||||
soundio->backend_data = nullptr;
|
||||
si->backend_data = nullptr;
|
||||
}
|
||||
|
||||
static char * str_partition_on_char(char *str, char c) {
|
||||
|
@ -132,28 +136,43 @@ static SoundIoChannelId from_alsa_chmap_pos(unsigned int pos) {
|
|||
return SoundIoChannelIdInvalid;
|
||||
}
|
||||
|
||||
static void get_channel_layout(SoundIoDevice *device, snd_pcm_chmap_t *chmap) {
|
||||
static void get_channel_layout(SoundIoChannelLayout *dest, snd_pcm_chmap_t *chmap) {
|
||||
int channel_count = min((unsigned int)SOUNDIO_MAX_CHANNELS, chmap->channels);
|
||||
device->channel_layout.channel_count = channel_count;
|
||||
device->channel_layout.name = nullptr;
|
||||
dest->channel_count = channel_count;
|
||||
for (int i = 0; i < channel_count; i += 1) {
|
||||
device->channel_layout.channels[i] = from_alsa_chmap_pos(chmap->pos[i]);
|
||||
dest->channels[i] = from_alsa_chmap_pos(chmap->pos[i]);
|
||||
}
|
||||
soundio_channel_layout_detect_builtin(&device->channel_layout);
|
||||
soundio_channel_layout_detect_builtin(dest);
|
||||
}
|
||||
|
||||
static void handle_channel_maps(SoundIoDevice *device, snd_pcm_chmap_query_t **maps) {
|
||||
static int handle_channel_maps(SoundIoDevice *device, snd_pcm_chmap_query_t **maps) {
|
||||
if (!maps)
|
||||
return;
|
||||
return 0;
|
||||
|
||||
snd_pcm_chmap_query_t **p;
|
||||
snd_pcm_chmap_query_t *v;
|
||||
snd_pcm_chmap_t *best = nullptr;
|
||||
for (p = maps; (v = *p); p += 1) {
|
||||
if (!best || v->map.channels > best->channels)
|
||||
best = &v->map;
|
||||
|
||||
// one iteration to count
|
||||
int layout_count = 0;
|
||||
for (p = maps; (v = *p) && layout_count < SOUNDIO_MAX_CHANNELS; p += 1, layout_count += 1) { }
|
||||
device->layouts = allocate<SoundIoChannelLayout>(layout_count);
|
||||
if (!device->layouts) {
|
||||
snd_pcm_free_chmaps(maps);
|
||||
return SoundIoErrorNoMem;
|
||||
}
|
||||
device->layout_count = layout_count;
|
||||
|
||||
// iterate again to collect data
|
||||
int layout_index;
|
||||
for (p = maps, layout_index = 0;
|
||||
(v = *p) && layout_index < layout_count;
|
||||
p += 1, layout_index += 1)
|
||||
{
|
||||
get_channel_layout(&device->layouts[layout_index], &v->map);
|
||||
}
|
||||
get_channel_layout(device, best);
|
||||
snd_pcm_free_chmaps(maps);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static snd_pcm_format_t to_alsa_fmt(SoundIoFormat fmt) {
|
||||
|
@ -196,68 +215,40 @@ static void test_fmt_mask(SoundIoDevice *device, const snd_pcm_format_mask_t *fm
|
|||
// * hw period time
|
||||
// * sw start threshold
|
||||
// * sw avail min
|
||||
// TODO: device->default_latency
|
||||
|
||||
static int probe_device(SoundIoDevice *device, snd_pcm_chmap_query_t **maps) {
|
||||
|
||||
// this function does not override device->formats, so if you want it to, deallocate and set it to NULL
|
||||
static int probe_open_device(SoundIoDevice *device, snd_pcm_t *handle,
|
||||
snd_pcm_hw_params_t *hwparams, int resample)
|
||||
{
|
||||
int err;
|
||||
snd_pcm_t *handle;
|
||||
|
||||
snd_pcm_hw_params_t *hwparams;
|
||||
snd_pcm_sw_params_t *swparams;
|
||||
|
||||
snd_pcm_hw_params_alloca(&hwparams);
|
||||
snd_pcm_sw_params_alloca(&swparams);
|
||||
|
||||
snd_pcm_stream_t stream = purpose_to_stream(device->purpose);
|
||||
|
||||
if ((err = snd_pcm_open(&handle, device->name, stream, 0)) < 0) {
|
||||
handle_channel_maps(device, maps);
|
||||
if ((err = snd_pcm_hw_params_any(handle, hwparams)) < 0)
|
||||
return SoundIoErrorOpeningDevice;
|
||||
}
|
||||
|
||||
if ((err = snd_pcm_hw_params_any(handle, hwparams)) < 0) {
|
||||
handle_channel_maps(device, maps);
|
||||
snd_pcm_close(handle);
|
||||
if ((err = snd_pcm_hw_params_set_rate_resample(handle, hwparams, resample)) < 0)
|
||||
return SoundIoErrorOpeningDevice;
|
||||
}
|
||||
|
||||
// disable hardware resampling because we're trying to probe
|
||||
if ((err = snd_pcm_hw_params_set_rate_resample(handle, hwparams, 0)) < 0) {
|
||||
handle_channel_maps(device, maps);
|
||||
snd_pcm_close(handle);
|
||||
if ((err = snd_pcm_hw_params_set_access(handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0)
|
||||
return SoundIoErrorOpeningDevice;
|
||||
}
|
||||
|
||||
if ((err = snd_pcm_hw_params_set_access(handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) {
|
||||
handle_channel_maps(device, maps);
|
||||
snd_pcm_close(handle);
|
||||
return SoundIoErrorOpeningDevice;
|
||||
}
|
||||
|
||||
unsigned int channel_count;
|
||||
if ((err = snd_pcm_hw_params_set_channels_last(handle, hwparams, &channel_count)) < 0) {
|
||||
handle_channel_maps(device, maps);
|
||||
snd_pcm_close(handle);
|
||||
if ((err = snd_pcm_hw_params_set_channels_last(handle, hwparams, &channel_count)) < 0)
|
||||
return SoundIoErrorOpeningDevice;
|
||||
}
|
||||
|
||||
unsigned int min_sample_rate;
|
||||
unsigned int max_sample_rate;
|
||||
int min_dir;
|
||||
int max_dir;
|
||||
|
||||
if ((err = snd_pcm_hw_params_get_rate_max(hwparams, &max_sample_rate, &max_dir)) < 0) {
|
||||
handle_channel_maps(device, maps);
|
||||
snd_pcm_close(handle);
|
||||
if ((err = snd_pcm_hw_params_get_rate_max(hwparams, &max_sample_rate, &max_dir)) < 0)
|
||||
return SoundIoErrorOpeningDevice;
|
||||
}
|
||||
if (max_dir < 0)
|
||||
max_sample_rate -= 1;
|
||||
|
||||
if ((err = snd_pcm_hw_params_get_rate_min(hwparams, &min_sample_rate, &min_dir)) < 0) {
|
||||
handle_channel_maps(device, maps);
|
||||
snd_pcm_close(handle);
|
||||
if ((err = snd_pcm_hw_params_get_rate_min(hwparams, &min_sample_rate, &min_dir)) < 0)
|
||||
return SoundIoErrorOpeningDevice;
|
||||
}
|
||||
if (min_dir > 0)
|
||||
min_sample_rate += 1;
|
||||
|
||||
|
@ -283,69 +274,100 @@ static int probe_device(SoundIoDevice *device, snd_pcm_chmap_query_t **maps) {
|
|||
snd_pcm_format_mask_set(fmt_mask, SND_PCM_FORMAT_FLOAT64_LE);
|
||||
snd_pcm_format_mask_set(fmt_mask, SND_PCM_FORMAT_FLOAT64_BE);
|
||||
|
||||
if ((err = snd_pcm_hw_params_set_format_mask(handle, hwparams, fmt_mask)) < 0) {
|
||||
handle_channel_maps(device, maps);
|
||||
snd_pcm_close(handle);
|
||||
if ((err = snd_pcm_hw_params_set_format_mask(handle, hwparams, fmt_mask)) < 0)
|
||||
return SoundIoErrorOpeningDevice;
|
||||
}
|
||||
|
||||
snd_pcm_hw_params_get_format_mask(hwparams, fmt_mask);
|
||||
device->formats = allocate<SoundIoFormat>(18);
|
||||
if (!device->formats) {
|
||||
handle_channel_maps(device, maps);
|
||||
snd_pcm_close(handle);
|
||||
return SoundIoErrorNoMem;
|
||||
snd_pcm_hw_params_get_format_mask(hwparams, fmt_mask);
|
||||
device->formats = allocate<SoundIoFormat>(18);
|
||||
if (!device->formats)
|
||||
return SoundIoErrorNoMem;
|
||||
|
||||
device->format_count = 0;
|
||||
test_fmt_mask(device, fmt_mask, SoundIoFormatS8);
|
||||
test_fmt_mask(device, fmt_mask, SoundIoFormatU8);
|
||||
test_fmt_mask(device, fmt_mask, SoundIoFormatS16LE);
|
||||
test_fmt_mask(device, fmt_mask, SoundIoFormatS16BE);
|
||||
test_fmt_mask(device, fmt_mask, SoundIoFormatU16LE);
|
||||
test_fmt_mask(device, fmt_mask, SoundIoFormatU16BE);
|
||||
test_fmt_mask(device, fmt_mask, SoundIoFormatS24LE);
|
||||
test_fmt_mask(device, fmt_mask, SoundIoFormatS24BE);
|
||||
test_fmt_mask(device, fmt_mask, SoundIoFormatU24LE);
|
||||
test_fmt_mask(device, fmt_mask, SoundIoFormatU24BE);
|
||||
test_fmt_mask(device, fmt_mask, SoundIoFormatS32LE);
|
||||
test_fmt_mask(device, fmt_mask, SoundIoFormatS32BE);
|
||||
test_fmt_mask(device, fmt_mask, SoundIoFormatU32LE);
|
||||
test_fmt_mask(device, fmt_mask, SoundIoFormatU32BE);
|
||||
test_fmt_mask(device, fmt_mask, SoundIoFormatFloat32LE);
|
||||
test_fmt_mask(device, fmt_mask, SoundIoFormatFloat32BE);
|
||||
test_fmt_mask(device, fmt_mask, SoundIoFormatFloat64LE);
|
||||
test_fmt_mask(device, fmt_mask, SoundIoFormatFloat64BE);
|
||||
}
|
||||
|
||||
device->format_count = 0;
|
||||
test_fmt_mask(device, fmt_mask, SoundIoFormatS8);
|
||||
test_fmt_mask(device, fmt_mask, SoundIoFormatU8);
|
||||
test_fmt_mask(device, fmt_mask, SoundIoFormatS16LE);
|
||||
test_fmt_mask(device, fmt_mask, SoundIoFormatS16BE);
|
||||
test_fmt_mask(device, fmt_mask, SoundIoFormatU16LE);
|
||||
test_fmt_mask(device, fmt_mask, SoundIoFormatU16BE);
|
||||
test_fmt_mask(device, fmt_mask, SoundIoFormatS24LE);
|
||||
test_fmt_mask(device, fmt_mask, SoundIoFormatS24BE);
|
||||
test_fmt_mask(device, fmt_mask, SoundIoFormatU24LE);
|
||||
test_fmt_mask(device, fmt_mask, SoundIoFormatU24BE);
|
||||
test_fmt_mask(device, fmt_mask, SoundIoFormatS32LE);
|
||||
test_fmt_mask(device, fmt_mask, SoundIoFormatS32BE);
|
||||
test_fmt_mask(device, fmt_mask, SoundIoFormatU32LE);
|
||||
test_fmt_mask(device, fmt_mask, SoundIoFormatU32BE);
|
||||
test_fmt_mask(device, fmt_mask, SoundIoFormatFloat32LE);
|
||||
test_fmt_mask(device, fmt_mask, SoundIoFormatFloat32BE);
|
||||
test_fmt_mask(device, fmt_mask, SoundIoFormatFloat64LE);
|
||||
test_fmt_mask(device, fmt_mask, SoundIoFormatFloat64BE);
|
||||
|
||||
|
||||
snd_pcm_chmap_t *chmap = snd_pcm_get_chmap(handle);
|
||||
if (chmap) {
|
||||
get_channel_layout(device, chmap);
|
||||
free(chmap);
|
||||
} else if (!maps) {
|
||||
maps = snd_pcm_query_chmaps(handle);
|
||||
}
|
||||
handle_channel_maps(device, maps);
|
||||
|
||||
|
||||
device->sample_rate_min = min_sample_rate;
|
||||
device->sample_rate_max = max_sample_rate;
|
||||
// TODO can we figure out what sample rate dmix is currently playing at,
|
||||
// if dmix applies to this device?
|
||||
device->sample_rate_current = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int probe_device(SoundIoDevice *device, snd_pcm_chmap_query_t **maps) {
|
||||
int err;
|
||||
snd_pcm_t *handle;
|
||||
|
||||
snd_pcm_hw_params_t *hwparams;
|
||||
snd_pcm_sw_params_t *swparams;
|
||||
|
||||
snd_pcm_hw_params_alloca(&hwparams);
|
||||
snd_pcm_sw_params_alloca(&swparams);
|
||||
|
||||
snd_pcm_stream_t stream = purpose_to_stream(device->purpose);
|
||||
|
||||
if ((err = snd_pcm_open(&handle, device->name, stream, 0)) < 0) {
|
||||
handle_channel_maps(device, maps);
|
||||
return SoundIoErrorOpeningDevice;
|
||||
}
|
||||
|
||||
if ((err = probe_open_device(device, handle, hwparams, 0))) {
|
||||
handle_channel_maps(device, maps);
|
||||
snd_pcm_close(handle);
|
||||
return err;
|
||||
}
|
||||
|
||||
if (!maps)
|
||||
maps = snd_pcm_query_chmaps(handle);
|
||||
|
||||
snd_pcm_chmap_t *chmap = snd_pcm_get_chmap(handle);
|
||||
if (chmap) {
|
||||
get_channel_layout(&device->current_layout, chmap);
|
||||
free(chmap);
|
||||
}
|
||||
if ((err = handle_channel_maps(device, maps))) {
|
||||
snd_pcm_close(handle);
|
||||
return err;
|
||||
}
|
||||
maps = nullptr;
|
||||
|
||||
if (device->sample_rate_min == device->sample_rate_max && !device->is_raw) {
|
||||
device->sample_rate_current = device->sample_rate_min;
|
||||
|
||||
// now say that resampling is OK and see what the real min and max is.
|
||||
if ((err = probe_open_device(device, handle, hwparams, 1)) < 0) {
|
||||
snd_pcm_close(handle);
|
||||
return SoundIoErrorOpeningDevice;
|
||||
}
|
||||
}
|
||||
|
||||
snd_pcm_close(handle);
|
||||
return 0;
|
||||
|
||||
// TODO: device->default_latency
|
||||
}
|
||||
|
||||
static inline bool str_has_prefix(const char *big_str, const char *prefix) {
|
||||
return strncmp(big_str, prefix, strlen(prefix)) == 0;
|
||||
}
|
||||
|
||||
static int refresh_devices(SoundIo *soundio) {
|
||||
SoundIoAlsa *sia = (SoundIoAlsa *)soundio->backend_data;
|
||||
static int refresh_devices(SoundIoPrivate *si) {
|
||||
SoundIo *soundio = &si->pub;
|
||||
SoundIoAlsa *sia = (SoundIoAlsa *)si->backend_data;
|
||||
|
||||
SoundIoDevicesInfo *devices_info = create<SoundIoDevicesInfo>();
|
||||
if (!devices_info)
|
||||
|
@ -585,8 +607,8 @@ static int refresh_devices(SoundIo *soundio) {
|
|||
}
|
||||
|
||||
static void device_thread_run(void *arg) {
|
||||
SoundIo *soundio = (SoundIo *)arg;
|
||||
SoundIoAlsa *sia = (SoundIoAlsa *)soundio->backend_data;
|
||||
SoundIoPrivate *si = (SoundIoPrivate *)arg;
|
||||
SoundIoAlsa *sia = (SoundIoAlsa *)si->backend_data;
|
||||
|
||||
// Some systems cannot read integer variables if they are not
|
||||
// properly aligned. On other systems, incorrect alignment may
|
||||
|
@ -672,7 +694,7 @@ static void device_thread_run(void *arg) {
|
|||
}
|
||||
}
|
||||
if (got_rescan_event) {
|
||||
if ((err = refresh_devices(soundio)))
|
||||
if ((err = refresh_devices(si)))
|
||||
soundio_panic("error refreshing devices: %s", soundio_strerror(err));
|
||||
}
|
||||
}
|
||||
|
@ -687,8 +709,9 @@ static void block_until_have_devices(SoundIoAlsa *sia) {
|
|||
soundio_os_mutex_unlock(sia->mutex);
|
||||
}
|
||||
|
||||
static void flush_events(SoundIo *soundio) {
|
||||
SoundIoAlsa *sia = (SoundIoAlsa *)soundio->backend_data;
|
||||
static void flush_events(SoundIoPrivate *si) {
|
||||
SoundIo *soundio = &si->pub;
|
||||
SoundIoAlsa *sia = (SoundIoAlsa *)si->backend_data;
|
||||
block_until_have_devices(sia);
|
||||
|
||||
bool change = false;
|
||||
|
@ -697,8 +720,8 @@ static void flush_events(SoundIo *soundio) {
|
|||
soundio_os_mutex_lock(sia->mutex);
|
||||
|
||||
if (sia->ready_devices_info) {
|
||||
old_devices_info = soundio->safe_devices_info;
|
||||
soundio->safe_devices_info = sia->ready_devices_info;
|
||||
old_devices_info = si->safe_devices_info;
|
||||
si->safe_devices_info = sia->ready_devices_info;
|
||||
sia->ready_devices_info = nullptr;
|
||||
change = true;
|
||||
}
|
||||
|
@ -711,109 +734,103 @@ static void flush_events(SoundIo *soundio) {
|
|||
soundio_destroy_devices_info(old_devices_info);
|
||||
}
|
||||
|
||||
static void wait_events(SoundIo *soundio) {
|
||||
SoundIoAlsa *sia = (SoundIoAlsa *)soundio->backend_data;
|
||||
flush_events(soundio);
|
||||
static void wait_events(SoundIoPrivate *si) {
|
||||
SoundIoAlsa *sia = (SoundIoAlsa *)si->backend_data;
|
||||
flush_events(si);
|
||||
soundio_os_mutex_lock(sia->mutex);
|
||||
soundio_os_cond_wait(sia->cond, sia->mutex);
|
||||
soundio_os_mutex_unlock(sia->mutex);
|
||||
}
|
||||
|
||||
static void wakeup(SoundIo *soundio) {
|
||||
SoundIoAlsa *sia = (SoundIoAlsa *)soundio->backend_data;
|
||||
static void wakeup(SoundIoPrivate *si) {
|
||||
SoundIoAlsa *sia = (SoundIoAlsa *)si->backend_data;
|
||||
soundio_os_mutex_lock(sia->mutex);
|
||||
soundio_os_cond_signal(sia->cond, sia->mutex);
|
||||
soundio_os_mutex_unlock(sia->mutex);
|
||||
}
|
||||
|
||||
static void out_stream_destroy_alsa(SoundIo *soundio,
|
||||
SoundIoOutStream *out_stream)
|
||||
{
|
||||
soundio_panic("TODO");
|
||||
static void outstream_destroy_alsa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os) {
|
||||
SoundIoOutStreamAlsa *osa = (SoundIoOutStreamAlsa *) os->backend_data;
|
||||
if (!osa)
|
||||
return;
|
||||
|
||||
destroy(osa);
|
||||
os->backend_data = nullptr;
|
||||
}
|
||||
|
||||
static int out_stream_init_alsa(SoundIo *soundio,
|
||||
SoundIoOutStream *out_stream)
|
||||
{
|
||||
soundio_panic("TODO");
|
||||
}
|
||||
|
||||
static int out_stream_start_alsa(SoundIo *soundio,
|
||||
SoundIoOutStream *out_stream)
|
||||
{
|
||||
soundio_panic("TODO");
|
||||
}
|
||||
|
||||
static int out_stream_free_count_alsa(SoundIo *soundio,
|
||||
SoundIoOutStream *out_stream)
|
||||
{
|
||||
soundio_panic("TODO");
|
||||
}
|
||||
|
||||
static void out_stream_begin_write_alsa(SoundIo *soundio,
|
||||
SoundIoOutStream *out_stream, char **data, int *frame_count)
|
||||
{
|
||||
soundio_panic("TODO");
|
||||
}
|
||||
|
||||
static void out_stream_write_alsa(SoundIo *soundio,
|
||||
SoundIoOutStream *out_stream, char *data, int frame_count)
|
||||
{
|
||||
soundio_panic("TODO");
|
||||
}
|
||||
|
||||
static void out_stream_clear_buffer_alsa(SoundIo *soundio,
|
||||
SoundIoOutStream *out_stream)
|
||||
{
|
||||
soundio_panic("TODO");
|
||||
}
|
||||
|
||||
static int in_stream_init_alsa(SoundIo *soundio,
|
||||
SoundIoInStream *in_stream)
|
||||
{
|
||||
soundio_panic("TODO");
|
||||
}
|
||||
|
||||
static void in_stream_destroy_alsa(SoundIo *soundio,
|
||||
SoundIoInStream *in_stream)
|
||||
{
|
||||
soundio_panic("TODO");
|
||||
}
|
||||
|
||||
static int in_stream_start_alsa(SoundIo *soundio,
|
||||
SoundIoInStream *in_stream)
|
||||
{
|
||||
soundio_panic("TODO");
|
||||
}
|
||||
|
||||
static void in_stream_peek_alsa(SoundIo *soundio,
|
||||
SoundIoInStream *in_stream, const char **data, int *frame_count)
|
||||
{
|
||||
soundio_panic("TODO");
|
||||
}
|
||||
|
||||
static void in_stream_drop_alsa(SoundIo *soundio,
|
||||
SoundIoInStream *in_stream)
|
||||
{
|
||||
soundio_panic("TODO");
|
||||
}
|
||||
|
||||
static void in_stream_clear_buffer_alsa(SoundIo *soundio,
|
||||
SoundIoInStream *in_stream)
|
||||
{
|
||||
soundio_panic("TODO");
|
||||
}
|
||||
|
||||
int soundio_alsa_init(SoundIo *soundio) {
|
||||
int err;
|
||||
|
||||
assert(!soundio->backend_data);
|
||||
SoundIoAlsa *sia = create<SoundIoAlsa>();
|
||||
if (!sia) {
|
||||
destroy_alsa(soundio);
|
||||
static int outstream_init_alsa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os) {
|
||||
SoundIoOutStreamAlsa *osa = create<SoundIoOutStreamAlsa>();
|
||||
if (!osa) {
|
||||
outstream_destroy_alsa(si, os);
|
||||
return SoundIoErrorNoMem;
|
||||
}
|
||||
soundio->backend_data = sia;
|
||||
os->backend_data = osa;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int outstream_start_alsa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os) {
|
||||
soundio_panic("TODO");
|
||||
}
|
||||
|
||||
static int outstream_free_count_alsa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os) {
|
||||
soundio_panic("TODO");
|
||||
}
|
||||
|
||||
static void outstream_begin_write_alsa(SoundIoPrivate *si,
|
||||
SoundIoOutStreamPrivate *os, char **data, int *frame_count)
|
||||
{
|
||||
soundio_panic("TODO");
|
||||
}
|
||||
|
||||
static void outstream_write_alsa(SoundIoPrivate *si,
|
||||
SoundIoOutStreamPrivate *os, char *data, int frame_count)
|
||||
{
|
||||
soundio_panic("TODO");
|
||||
}
|
||||
|
||||
static void outstream_clear_buffer_alsa(SoundIoPrivate *si,
|
||||
SoundIoOutStreamPrivate *os)
|
||||
{
|
||||
soundio_panic("TODO");
|
||||
}
|
||||
|
||||
static int instream_init_alsa(SoundIoPrivate *si, SoundIoInStreamPrivate *is) {
|
||||
soundio_panic("TODO");
|
||||
}
|
||||
|
||||
static void instream_destroy_alsa(SoundIoPrivate *si, SoundIoInStreamPrivate *is) {
|
||||
soundio_panic("TODO");
|
||||
}
|
||||
|
||||
static int instream_start_alsa(SoundIoPrivate *si, SoundIoInStreamPrivate *is) {
|
||||
soundio_panic("TODO");
|
||||
}
|
||||
|
||||
static void instream_peek_alsa(SoundIoPrivate *si,
|
||||
SoundIoInStreamPrivate *is, const char **data, int *frame_count)
|
||||
{
|
||||
soundio_panic("TODO");
|
||||
}
|
||||
|
||||
static void instream_drop_alsa(SoundIoPrivate *si, SoundIoInStreamPrivate *is) {
|
||||
soundio_panic("TODO");
|
||||
}
|
||||
|
||||
static void instream_clear_buffer_alsa(SoundIoPrivate *si, SoundIoInStreamPrivate *is) {
|
||||
soundio_panic("TODO");
|
||||
}
|
||||
|
||||
int soundio_alsa_init(SoundIoPrivate *si) {
|
||||
int err;
|
||||
|
||||
assert(!si->backend_data);
|
||||
SoundIoAlsa *sia = create<SoundIoAlsa>();
|
||||
if (!sia) {
|
||||
destroy_alsa(si);
|
||||
return SoundIoErrorNoMem;
|
||||
}
|
||||
si->backend_data = sia;
|
||||
sia->notify_fd = -1;
|
||||
sia->notify_wd = -1;
|
||||
sia->have_devices_flag.store(false);
|
||||
|
@ -821,13 +838,13 @@ int soundio_alsa_init(SoundIo *soundio) {
|
|||
|
||||
sia->mutex = soundio_os_mutex_create();
|
||||
if (!sia->mutex) {
|
||||
destroy_alsa(soundio);
|
||||
destroy_alsa(si);
|
||||
return SoundIoErrorNoMem;
|
||||
}
|
||||
|
||||
sia->cond = soundio_os_cond_create();
|
||||
if (!sia->cond) {
|
||||
destroy_alsa(soundio);
|
||||
destroy_alsa(si);
|
||||
return SoundIoErrorNoMem;
|
||||
}
|
||||
|
||||
|
@ -837,7 +854,7 @@ int soundio_alsa_init(SoundIo *soundio) {
|
|||
if (sia->notify_fd == -1) {
|
||||
err = errno;
|
||||
assert(err != EINVAL);
|
||||
destroy_alsa(soundio);
|
||||
destroy_alsa(si);
|
||||
if (err == EMFILE || err == ENFILE) {
|
||||
return SoundIoErrorSystemResources;
|
||||
} else {
|
||||
|
@ -855,7 +872,7 @@ int soundio_alsa_init(SoundIo *soundio) {
|
|||
assert(err != EINVAL);
|
||||
assert(err != ENAMETOOLONG);
|
||||
assert(err != ENOENT);
|
||||
destroy_alsa(soundio);
|
||||
destroy_alsa(si);
|
||||
if (err == ENOSPC) {
|
||||
return SoundIoErrorSystemResources;
|
||||
} else {
|
||||
|
@ -873,30 +890,30 @@ int soundio_alsa_init(SoundIo *soundio) {
|
|||
|
||||
wakeup_device_poll(sia);
|
||||
|
||||
if ((err = soundio_os_thread_create(device_thread_run, soundio, false, &sia->thread))) {
|
||||
destroy_alsa(soundio);
|
||||
if ((err = soundio_os_thread_create(device_thread_run, si, false, &sia->thread))) {
|
||||
destroy_alsa(si);
|
||||
return err;
|
||||
}
|
||||
|
||||
soundio->destroy = destroy_alsa;
|
||||
soundio->flush_events = flush_events;
|
||||
soundio->wait_events = wait_events;
|
||||
soundio->wakeup = wakeup;
|
||||
si->destroy = destroy_alsa;
|
||||
si->flush_events = flush_events;
|
||||
si->wait_events = wait_events;
|
||||
si->wakeup = wakeup;
|
||||
|
||||
soundio->out_stream_init = out_stream_init_alsa;
|
||||
soundio->out_stream_destroy = out_stream_destroy_alsa;
|
||||
soundio->out_stream_start = out_stream_start_alsa;
|
||||
soundio->out_stream_free_count = out_stream_free_count_alsa;
|
||||
soundio->out_stream_begin_write = out_stream_begin_write_alsa;
|
||||
soundio->out_stream_write = out_stream_write_alsa;
|
||||
soundio->out_stream_clear_buffer = out_stream_clear_buffer_alsa;
|
||||
si->outstream_init = outstream_init_alsa;
|
||||
si->outstream_destroy = outstream_destroy_alsa;
|
||||
si->outstream_start = outstream_start_alsa;
|
||||
si->outstream_free_count = outstream_free_count_alsa;
|
||||
si->outstream_begin_write = outstream_begin_write_alsa;
|
||||
si->outstream_write = outstream_write_alsa;
|
||||
si->outstream_clear_buffer = outstream_clear_buffer_alsa;
|
||||
|
||||
soundio->in_stream_init = in_stream_init_alsa;
|
||||
soundio->in_stream_destroy = in_stream_destroy_alsa;
|
||||
soundio->in_stream_start = in_stream_start_alsa;
|
||||
soundio->in_stream_peek = in_stream_peek_alsa;
|
||||
soundio->in_stream_drop = in_stream_drop_alsa;
|
||||
soundio->in_stream_clear_buffer = in_stream_clear_buffer_alsa;
|
||||
si->instream_init = instream_init_alsa;
|
||||
si->instream_destroy = instream_destroy_alsa;
|
||||
si->instream_start = instream_start_alsa;
|
||||
si->instream_peek = instream_peek_alsa;
|
||||
si->instream_drop = instream_drop_alsa;
|
||||
si->instream_clear_buffer = instream_clear_buffer_alsa;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
#ifndef SOUNDIO_ALSA_HPP
|
||||
#define SOUNDIO_ALSA_HPP
|
||||
|
||||
int soundio_alsa_init(struct SoundIo *soundio);
|
||||
int soundio_alsa_init(struct SoundIoPrivate *si);
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
@ -310,7 +310,6 @@ static struct SoundIoChannelLayout builtin_channel_layouts[] = {
|
|||
const char *soundio_get_channel_name(enum SoundIoChannelId id) {
|
||||
switch (id) {
|
||||
case SoundIoChannelIdInvalid: return "(Invalid Channel)";
|
||||
case SoundIoChannelIdCount: return "(Invalid Channel)";
|
||||
|
||||
case SoundIoChannelIdFrontLeft: return "Front Left";
|
||||
case SoundIoChannelIdFrontRight: return "Front Right";
|
||||
|
@ -405,5 +404,6 @@ bool soundio_channel_layout_detect_builtin(struct SoundIoChannelLayout *layout)
|
|||
return true;
|
||||
}
|
||||
}
|
||||
layout->name = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
|
252
src/dummy.cpp
252
src/dummy.cpp
|
@ -34,13 +34,14 @@ struct SoundIoDummy {
|
|||
};
|
||||
|
||||
static void playback_thread_run(void *arg) {
|
||||
SoundIoOutStream *out_stream = (SoundIoOutStream *)arg;
|
||||
SoundIoOutStreamDummy *osd = (SoundIoOutStreamDummy *)out_stream->backend_data;
|
||||
SoundIoOutStreamPrivate *os = (SoundIoOutStreamPrivate *)arg;
|
||||
SoundIoOutStream *outstream = &os->pub;
|
||||
SoundIoOutStreamDummy *osd = (SoundIoOutStreamDummy *)os->backend_data;
|
||||
|
||||
double start_time = soundio_os_get_time();
|
||||
long frames_consumed = 0;
|
||||
|
||||
double time_per_frame = 1.0 / (double)out_stream->sample_rate;
|
||||
double time_per_frame = 1.0 / (double)outstream->sample_rate;
|
||||
while (osd->abort_flag.test_and_set()) {
|
||||
soundio_os_cond_timed_wait(osd->cond, nullptr, osd->period);
|
||||
|
||||
|
@ -49,32 +50,32 @@ static void playback_thread_run(void *arg) {
|
|||
long total_frames = total_time / time_per_frame;
|
||||
int frames_to_kill = total_frames - frames_consumed;
|
||||
int fill_count = soundio_ring_buffer_fill_count(&osd->ring_buffer);
|
||||
int frames_in_buffer = fill_count / out_stream->bytes_per_frame;
|
||||
int frames_in_buffer = fill_count / outstream->bytes_per_frame;
|
||||
int read_count = min(frames_to_kill, frames_in_buffer);
|
||||
int frames_left = frames_to_kill - read_count;
|
||||
int byte_count = read_count * out_stream->bytes_per_frame;
|
||||
int byte_count = read_count * outstream->bytes_per_frame;
|
||||
soundio_ring_buffer_advance_read_ptr(&osd->ring_buffer, byte_count);
|
||||
frames_consumed += read_count;
|
||||
|
||||
if (frames_left > 0) {
|
||||
out_stream->underrun_callback(out_stream);
|
||||
outstream->underrun_callback(outstream);
|
||||
} else if (read_count > 0) {
|
||||
out_stream->write_callback(out_stream, read_count);
|
||||
outstream->write_callback(outstream, read_count);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
static void recording_thread_run(void *arg) {
|
||||
SoundIoInStream *in_stream = (SoundIoInStream *)arg;
|
||||
SoundIoDevice *device = in_stream->device;
|
||||
SoundIoInStream *instream = (SoundIoInStream *)arg;
|
||||
SoundIoDevice *device = instream->device;
|
||||
SoundIo *soundio = device->soundio;
|
||||
// TODO
|
||||
}
|
||||
*/
|
||||
|
||||
static void destroy_dummy(SoundIo *soundio) {
|
||||
SoundIoDummy *sid = (SoundIoDummy *)soundio->backend_data;
|
||||
static void destroy_dummy(SoundIoPrivate *si) {
|
||||
SoundIoDummy *sid = (SoundIoDummy *)si->backend_data;
|
||||
if (!sid)
|
||||
return;
|
||||
|
||||
|
@ -85,32 +86,31 @@ static void destroy_dummy(SoundIo *soundio) {
|
|||
soundio_os_mutex_destroy(sid->mutex);
|
||||
|
||||
destroy(sid);
|
||||
soundio->backend_data = nullptr;
|
||||
si->backend_data = nullptr;
|
||||
}
|
||||
|
||||
static void flush_events(SoundIo *soundio) {
|
||||
SoundIoDummy *sid = (SoundIoDummy *)soundio->backend_data;
|
||||
static void flush_events(SoundIoPrivate *si) {
|
||||
SoundIo *soundio = &si->pub;
|
||||
SoundIoDummy *sid = (SoundIoDummy *)si->backend_data;
|
||||
if (sid->devices_emitted)
|
||||
return;
|
||||
sid->devices_emitted = true;
|
||||
soundio->on_devices_change(soundio);
|
||||
}
|
||||
|
||||
static void wait_events(SoundIo *soundio) {
|
||||
SoundIoDummy *sid = (SoundIoDummy *)soundio->backend_data;
|
||||
flush_events(soundio);
|
||||
static void wait_events(SoundIoPrivate *si) {
|
||||
SoundIoDummy *sid = (SoundIoDummy *)si->backend_data;
|
||||
flush_events(si);
|
||||
soundio_os_cond_wait(sid->cond, nullptr);
|
||||
}
|
||||
|
||||
static void wakeup(SoundIo *soundio) {
|
||||
SoundIoDummy *sid = (SoundIoDummy *)soundio->backend_data;
|
||||
static void wakeup(SoundIoPrivate *si) {
|
||||
SoundIoDummy *sid = (SoundIoDummy *)si->backend_data;
|
||||
soundio_os_cond_signal(sid->cond, nullptr);
|
||||
}
|
||||
|
||||
static void out_stream_destroy_dummy(SoundIo *soundio,
|
||||
SoundIoOutStream *out_stream)
|
||||
{
|
||||
SoundIoOutStreamDummy *osd = (SoundIoOutStreamDummy *)out_stream->backend_data;
|
||||
static void outstream_destroy_dummy(SoundIoPrivate *si, SoundIoOutStreamPrivate *os) {
|
||||
SoundIoOutStreamDummy *osd = (SoundIoOutStreamDummy *)os->backend_data;
|
||||
if (!osd)
|
||||
return;
|
||||
|
||||
|
@ -128,120 +128,106 @@ static void out_stream_destroy_dummy(SoundIo *soundio,
|
|||
soundio_ring_buffer_deinit(&osd->ring_buffer);
|
||||
|
||||
destroy(osd);
|
||||
out_stream->backend_data = nullptr;
|
||||
os->backend_data = nullptr;
|
||||
}
|
||||
|
||||
static int out_stream_init_dummy(SoundIo *soundio,
|
||||
SoundIoOutStream *out_stream)
|
||||
{
|
||||
|
||||
static int outstream_init_dummy(SoundIoPrivate *si, SoundIoOutStreamPrivate *os) {
|
||||
SoundIoOutStream *outstream = &os->pub;
|
||||
SoundIoOutStreamDummy *osd = create<SoundIoOutStreamDummy>();
|
||||
if (!osd) {
|
||||
out_stream_destroy_dummy(soundio, out_stream);
|
||||
outstream_destroy_dummy(si, os);
|
||||
return SoundIoErrorNoMem;
|
||||
}
|
||||
out_stream->backend_data = osd;
|
||||
os->backend_data = osd;
|
||||
|
||||
int buffer_frame_count = out_stream->latency * out_stream->sample_rate;
|
||||
osd->buffer_size = out_stream->bytes_per_frame * buffer_frame_count;
|
||||
osd->period = out_stream->latency * 0.5;
|
||||
int buffer_frame_count = outstream->latency * outstream->sample_rate;
|
||||
osd->buffer_size = outstream->bytes_per_frame * buffer_frame_count;
|
||||
osd->period = outstream->latency * 0.5;
|
||||
|
||||
soundio_ring_buffer_init(&osd->ring_buffer, osd->buffer_size);
|
||||
|
||||
osd->cond = soundio_os_cond_create();
|
||||
if (!osd->cond) {
|
||||
out_stream_destroy_dummy(soundio, out_stream);
|
||||
outstream_destroy_dummy(si, os);
|
||||
return SoundIoErrorNoMem;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int out_stream_start_dummy(SoundIo *soundio,
|
||||
SoundIoOutStream *out_stream)
|
||||
{
|
||||
SoundIoOutStreamDummy *osd = (SoundIoOutStreamDummy *)out_stream->backend_data;
|
||||
static int outstream_start_dummy(SoundIoPrivate *soundio, SoundIoOutStreamPrivate *os) {
|
||||
SoundIoOutStream *outstream = &os->pub;
|
||||
SoundIoOutStreamDummy *osd = (SoundIoOutStreamDummy *)os->backend_data;
|
||||
|
||||
soundio_out_stream_fill_with_silence(out_stream);
|
||||
soundio_outstream_fill_with_silence(outstream);
|
||||
assert(soundio_ring_buffer_fill_count(&osd->ring_buffer) == osd->buffer_size);
|
||||
|
||||
osd->abort_flag.test_and_set();
|
||||
int err;
|
||||
if ((err = soundio_os_thread_create(playback_thread_run, out_stream, true, &osd->thread))) {
|
||||
if ((err = soundio_os_thread_create(playback_thread_run, os, true, &osd->thread))) {
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int out_stream_free_count_dummy(SoundIo *soundio,
|
||||
SoundIoOutStream *out_stream)
|
||||
{
|
||||
SoundIoOutStreamDummy *osd = (SoundIoOutStreamDummy *)out_stream->backend_data;
|
||||
static int outstream_free_count_dummy(SoundIoPrivate *soundio, SoundIoOutStreamPrivate *os) {
|
||||
SoundIoOutStream *outstream = &os->pub;
|
||||
SoundIoOutStreamDummy *osd = (SoundIoOutStreamDummy *)os->backend_data;
|
||||
int fill_count = soundio_ring_buffer_fill_count(&osd->ring_buffer);
|
||||
int bytes_free_count = osd->buffer_size - fill_count;
|
||||
return bytes_free_count / out_stream->bytes_per_frame;
|
||||
return bytes_free_count / outstream->bytes_per_frame;
|
||||
}
|
||||
|
||||
static void out_stream_begin_write_dummy(SoundIo *soundio,
|
||||
SoundIoOutStream *out_stream, char **data, int *frame_count)
|
||||
static void outstream_begin_write_dummy(SoundIoPrivate *si,
|
||||
SoundIoOutStreamPrivate *os, char **data, int *frame_count)
|
||||
{
|
||||
SoundIoOutStreamDummy *osd = (SoundIoOutStreamDummy *)out_stream->backend_data;
|
||||
SoundIoOutStream *outstream = &os->pub;
|
||||
SoundIoOutStreamDummy *osd = (SoundIoOutStreamDummy *)os->backend_data;
|
||||
|
||||
int byte_count = *frame_count * out_stream->bytes_per_frame;
|
||||
int byte_count = *frame_count * outstream->bytes_per_frame;
|
||||
assert(byte_count <= osd->buffer_size);
|
||||
*data = osd->ring_buffer.address;
|
||||
}
|
||||
|
||||
static void out_stream_write_dummy(SoundIo *soundio,
|
||||
SoundIoOutStream *out_stream, char *data, int frame_count)
|
||||
static void outstream_write_dummy(SoundIoPrivate *si,
|
||||
SoundIoOutStreamPrivate *os, char *data, int frame_count)
|
||||
{
|
||||
SoundIoOutStreamDummy *osd = (SoundIoOutStreamDummy *)out_stream->backend_data;
|
||||
SoundIoOutStreamDummy *osd = (SoundIoOutStreamDummy *)os->backend_data;
|
||||
SoundIoOutStream *outstream = &os->pub;
|
||||
assert(data == osd->ring_buffer.address);
|
||||
int byte_count = frame_count * out_stream->bytes_per_frame;
|
||||
int byte_count = frame_count * outstream->bytes_per_frame;
|
||||
soundio_ring_buffer_advance_write_ptr(&osd->ring_buffer, byte_count);
|
||||
}
|
||||
|
||||
static void out_stream_clear_buffer_dummy(SoundIo *soundio,
|
||||
SoundIoOutStream *out_stream)
|
||||
{
|
||||
SoundIoOutStreamDummy *osd = (SoundIoOutStreamDummy *)out_stream->backend_data;
|
||||
static void outstream_clear_buffer_dummy(SoundIoPrivate *si, SoundIoOutStreamPrivate *os) {
|
||||
SoundIoOutStreamDummy *osd = (SoundIoOutStreamDummy *)os->backend_data;
|
||||
soundio_ring_buffer_clear(&osd->ring_buffer);
|
||||
}
|
||||
|
||||
static int in_stream_init_dummy(SoundIo *soundio,
|
||||
SoundIoInStream *in_stream)
|
||||
static int instream_init_dummy(SoundIoPrivate *si, SoundIoInStreamPrivate *is) {
|
||||
soundio_panic("TODO");
|
||||
}
|
||||
|
||||
static void instream_destroy_dummy(SoundIoPrivate *si, SoundIoInStreamPrivate *is) {
|
||||
soundio_panic("TODO");
|
||||
}
|
||||
|
||||
static int instream_start_dummy(SoundIoPrivate *si, SoundIoInStreamPrivate *is) {
|
||||
soundio_panic("TODO");
|
||||
}
|
||||
|
||||
static void instream_peek_dummy(SoundIoPrivate *si,
|
||||
SoundIoInStreamPrivate *is, const char **data, int *frame_count)
|
||||
{
|
||||
soundio_panic("TODO");
|
||||
}
|
||||
|
||||
static void in_stream_destroy_dummy(SoundIo *soundio,
|
||||
SoundIoInStream *in_stream)
|
||||
{
|
||||
static void instream_drop_dummy(SoundIoPrivate *si, SoundIoInStreamPrivate *is) {
|
||||
soundio_panic("TODO");
|
||||
}
|
||||
|
||||
static int in_stream_start_dummy(SoundIo *soundio,
|
||||
SoundIoInStream *in_stream)
|
||||
{
|
||||
soundio_panic("TODO");
|
||||
}
|
||||
|
||||
static void in_stream_peek_dummy(SoundIo *soundio,
|
||||
SoundIoInStream *in_stream, const char **data, int *frame_count)
|
||||
{
|
||||
soundio_panic("TODO");
|
||||
}
|
||||
|
||||
static void in_stream_drop_dummy(SoundIo *soundio,
|
||||
SoundIoInStream *in_stream)
|
||||
{
|
||||
soundio_panic("TODO");
|
||||
}
|
||||
|
||||
static void in_stream_clear_buffer_dummy(SoundIo *soundio,
|
||||
SoundIoInStream *in_stream)
|
||||
{
|
||||
static void instream_clear_buffer_dummy(SoundIoPrivate *si, SoundIoInStreamPrivate *is) {
|
||||
soundio_panic("TODO");
|
||||
}
|
||||
|
||||
|
@ -273,42 +259,43 @@ static int set_all_device_formats(SoundIoDevice *device) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
int soundio_dummy_init(SoundIo *soundio) {
|
||||
assert(!soundio->backend_data);
|
||||
int soundio_dummy_init(SoundIoPrivate *si) {
|
||||
SoundIo *soundio = &si->pub;
|
||||
assert(!si->backend_data);
|
||||
SoundIoDummy *sid = create<SoundIoDummy>();
|
||||
if (!sid) {
|
||||
destroy_dummy(soundio);
|
||||
destroy_dummy(si);
|
||||
return SoundIoErrorNoMem;
|
||||
}
|
||||
soundio->backend_data = sid;
|
||||
si->backend_data = sid;
|
||||
|
||||
sid->mutex = soundio_os_mutex_create();
|
||||
if (!sid->mutex) {
|
||||
destroy_dummy(soundio);
|
||||
destroy_dummy(si);
|
||||
return SoundIoErrorNoMem;
|
||||
}
|
||||
|
||||
sid->cond = soundio_os_cond_create();
|
||||
if (!sid->cond) {
|
||||
destroy_dummy(soundio);
|
||||
destroy_dummy(si);
|
||||
return SoundIoErrorNoMem;
|
||||
}
|
||||
|
||||
assert(!soundio->safe_devices_info);
|
||||
soundio->safe_devices_info = create<SoundIoDevicesInfo>();
|
||||
if (!soundio->safe_devices_info) {
|
||||
destroy_dummy(soundio);
|
||||
assert(!si->safe_devices_info);
|
||||
si->safe_devices_info = create<SoundIoDevicesInfo>();
|
||||
if (!si->safe_devices_info) {
|
||||
destroy_dummy(si);
|
||||
return SoundIoErrorNoMem;
|
||||
}
|
||||
|
||||
soundio->safe_devices_info->default_input_index = 0;
|
||||
soundio->safe_devices_info->default_output_index = 0;
|
||||
si->safe_devices_info->default_input_index = 0;
|
||||
si->safe_devices_info->default_output_index = 0;
|
||||
|
||||
// create output device
|
||||
{
|
||||
SoundIoDevice *device = create<SoundIoDevice>();
|
||||
if (!device) {
|
||||
destroy_dummy(soundio);
|
||||
destroy_dummy(si);
|
||||
return SoundIoErrorNoMem;
|
||||
}
|
||||
|
||||
|
@ -318,14 +305,22 @@ int soundio_dummy_init(SoundIo *soundio) {
|
|||
device->description = strdup("Dummy Output Device");
|
||||
if (!device->name || !device->description) {
|
||||
soundio_device_unref(device);
|
||||
destroy_dummy(soundio);
|
||||
destroy_dummy(si);
|
||||
return SoundIoErrorNoMem;
|
||||
}
|
||||
device->channel_layout = *soundio_channel_layout_get_builtin(SoundIoChannelLayoutIdMono);
|
||||
device->layout_count = 1;
|
||||
device->layouts = allocate<SoundIoChannelLayout>(1);
|
||||
if (!device->layouts) {
|
||||
soundio_device_unref(device);
|
||||
destroy_dummy(si);
|
||||
return SoundIoErrorNoMem;
|
||||
}
|
||||
device->layouts[0] = *soundio_channel_layout_get_builtin(SoundIoChannelLayoutIdMono);
|
||||
|
||||
int err;
|
||||
if ((err = set_all_device_formats(device))) {
|
||||
soundio_device_unref(device);
|
||||
destroy_dummy(soundio);
|
||||
destroy_dummy(si);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
@ -335,9 +330,9 @@ int soundio_dummy_init(SoundIo *soundio) {
|
|||
device->sample_rate_current = 48000;
|
||||
device->purpose = SoundIoDevicePurposeOutput;
|
||||
|
||||
if (soundio->safe_devices_info->output_devices.append(device)) {
|
||||
if (si->safe_devices_info->output_devices.append(device)) {
|
||||
soundio_device_unref(device);
|
||||
destroy_dummy(soundio);
|
||||
destroy_dummy(si);
|
||||
return SoundIoErrorNoMem;
|
||||
}
|
||||
}
|
||||
|
@ -346,7 +341,7 @@ int soundio_dummy_init(SoundIo *soundio) {
|
|||
{
|
||||
SoundIoDevice *device = create<SoundIoDevice>();
|
||||
if (!device) {
|
||||
destroy_dummy(soundio);
|
||||
destroy_dummy(si);
|
||||
return SoundIoErrorNoMem;
|
||||
}
|
||||
|
||||
|
@ -356,14 +351,23 @@ int soundio_dummy_init(SoundIo *soundio) {
|
|||
device->description = strdup("Dummy input device");
|
||||
if (!device->name || !device->description) {
|
||||
soundio_device_unref(device);
|
||||
destroy_dummy(soundio);
|
||||
destroy_dummy(si);
|
||||
return SoundIoErrorNoMem;
|
||||
}
|
||||
device->channel_layout = *soundio_channel_layout_get_builtin(SoundIoChannelLayoutIdMono);
|
||||
|
||||
device->layout_count = 1;
|
||||
device->layouts = allocate<SoundIoChannelLayout>(1);
|
||||
if (!device->layouts) {
|
||||
soundio_device_unref(device);
|
||||
destroy_dummy(si);
|
||||
return SoundIoErrorNoMem;
|
||||
}
|
||||
device->layouts[0] = *soundio_channel_layout_get_builtin(SoundIoChannelLayoutIdMono);
|
||||
|
||||
int err;
|
||||
if ((err = set_all_device_formats(device))) {
|
||||
soundio_device_unref(device);
|
||||
destroy_dummy(soundio);
|
||||
destroy_dummy(si);
|
||||
return err;
|
||||
}
|
||||
device->default_latency = 0.01;
|
||||
|
@ -372,33 +376,33 @@ int soundio_dummy_init(SoundIo *soundio) {
|
|||
device->sample_rate_current = 48000;
|
||||
device->purpose = SoundIoDevicePurposeInput;
|
||||
|
||||
if (soundio->safe_devices_info->input_devices.append(device)) {
|
||||
if (si->safe_devices_info->input_devices.append(device)) {
|
||||
soundio_device_unref(device);
|
||||
destroy_dummy(soundio);
|
||||
destroy_dummy(si);
|
||||
return SoundIoErrorNoMem;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
soundio->destroy = destroy_dummy;
|
||||
soundio->flush_events = flush_events;
|
||||
soundio->wait_events = wait_events;
|
||||
soundio->wakeup = wakeup;
|
||||
si->destroy = destroy_dummy;
|
||||
si->flush_events = flush_events;
|
||||
si->wait_events = wait_events;
|
||||
si->wakeup = wakeup;
|
||||
|
||||
soundio->out_stream_init = out_stream_init_dummy;
|
||||
soundio->out_stream_destroy = out_stream_destroy_dummy;
|
||||
soundio->out_stream_start = out_stream_start_dummy;
|
||||
soundio->out_stream_free_count = out_stream_free_count_dummy;
|
||||
soundio->out_stream_begin_write = out_stream_begin_write_dummy;
|
||||
soundio->out_stream_write = out_stream_write_dummy;
|
||||
soundio->out_stream_clear_buffer = out_stream_clear_buffer_dummy;
|
||||
si->outstream_init = outstream_init_dummy;
|
||||
si->outstream_destroy = outstream_destroy_dummy;
|
||||
si->outstream_start = outstream_start_dummy;
|
||||
si->outstream_free_count = outstream_free_count_dummy;
|
||||
si->outstream_begin_write = outstream_begin_write_dummy;
|
||||
si->outstream_write = outstream_write_dummy;
|
||||
si->outstream_clear_buffer = outstream_clear_buffer_dummy;
|
||||
|
||||
soundio->in_stream_init = in_stream_init_dummy;
|
||||
soundio->in_stream_destroy = in_stream_destroy_dummy;
|
||||
soundio->in_stream_start = in_stream_start_dummy;
|
||||
soundio->in_stream_peek = in_stream_peek_dummy;
|
||||
soundio->in_stream_drop = in_stream_drop_dummy;
|
||||
soundio->in_stream_clear_buffer = in_stream_clear_buffer_dummy;
|
||||
si->instream_init = instream_init_dummy;
|
||||
si->instream_destroy = instream_destroy_dummy;
|
||||
si->instream_start = instream_start_dummy;
|
||||
si->instream_peek = instream_peek_dummy;
|
||||
si->instream_drop = instream_drop_dummy;
|
||||
si->instream_clear_buffer = instream_clear_buffer_dummy;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -8,6 +8,6 @@
|
|||
#ifndef SOUNDIO_DUMMY_HPP
|
||||
#define SOUNDIO_DUMMY_HPP
|
||||
|
||||
int soundio_dummy_init(struct SoundIo *soundio);
|
||||
int soundio_dummy_init(struct SoundIoPrivate *si);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -55,26 +55,26 @@ struct SoundIoPulseAudio {
|
|||
static void subscribe_callback(pa_context *context,
|
||||
pa_subscription_event_type_t event_bits, uint32_t index, void *userdata)
|
||||
{
|
||||
SoundIo *soundio = (SoundIo *)userdata;
|
||||
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)soundio->backend_data;
|
||||
SoundIoPrivate *si = (SoundIoPrivate *)userdata;
|
||||
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data;
|
||||
sipa->device_scan_queued = true;
|
||||
pa_threaded_mainloop_signal(sipa->main_loop, 0);
|
||||
}
|
||||
|
||||
static void subscribe_to_events(SoundIo *soundio) {
|
||||
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)soundio->backend_data;
|
||||
static void subscribe_to_events(SoundIoPrivate *si) {
|
||||
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data;
|
||||
pa_subscription_mask_t events = (pa_subscription_mask_t)(
|
||||
PA_SUBSCRIPTION_MASK_SINK|PA_SUBSCRIPTION_MASK_SOURCE|PA_SUBSCRIPTION_MASK_SERVER);
|
||||
pa_operation *subscribe_op = pa_context_subscribe(sipa->pulse_context,
|
||||
events, nullptr, soundio);
|
||||
events, nullptr, si);
|
||||
if (!subscribe_op)
|
||||
soundio_panic("pa_context_subscribe failed: %s", pa_strerror(pa_context_errno(sipa->pulse_context)));
|
||||
pa_operation_unref(subscribe_op);
|
||||
}
|
||||
|
||||
static void context_state_callback(pa_context *context, void *userdata) {
|
||||
SoundIo *soundio = (SoundIo *)userdata;
|
||||
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)soundio->backend_data;
|
||||
SoundIoPrivate *si = (SoundIoPrivate *)userdata;
|
||||
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data;
|
||||
|
||||
switch (pa_context_get_state(context)) {
|
||||
case PA_CONTEXT_UNCONNECTED: // The context hasn't been connected yet.
|
||||
|
@ -87,7 +87,7 @@ static void context_state_callback(pa_context *context, void *userdata) {
|
|||
return;
|
||||
case PA_CONTEXT_READY: // The connection is established, the context is ready to execute operations.
|
||||
sipa->device_scan_queued = true;
|
||||
subscribe_to_events(soundio);
|
||||
subscribe_to_events(si);
|
||||
sipa->ready_flag = true;
|
||||
pa_threaded_mainloop_signal(sipa->main_loop, 0);
|
||||
return;
|
||||
|
@ -107,8 +107,8 @@ static void context_state_callback(pa_context *context, void *userdata) {
|
|||
}
|
||||
}
|
||||
|
||||
static void destroy_pa(SoundIo *soundio) {
|
||||
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)soundio->backend_data;
|
||||
static void destroy_pa(SoundIoPrivate *si) {
|
||||
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data;
|
||||
if (!sipa)
|
||||
return;
|
||||
|
||||
|
@ -131,7 +131,7 @@ static void destroy_pa(SoundIo *soundio) {
|
|||
free(sipa->default_source_name);
|
||||
|
||||
destroy(sipa);
|
||||
soundio->backend_data = nullptr;
|
||||
si->backend_data = nullptr;
|
||||
}
|
||||
|
||||
static double usec_to_sec(pa_usec_t usec) {
|
||||
|
@ -166,6 +166,7 @@ static int sample_rate_from_pulseaudio(pa_sample_spec sample_spec) {
|
|||
return sample_spec.rate;
|
||||
}
|
||||
|
||||
/* TODO
|
||||
static SoundIoChannelId from_pulseaudio_channel_pos(pa_channel_position_t pos) {
|
||||
switch (pos) {
|
||||
case PA_CHANNEL_POSITION_MONO: return SoundIoChannelIdFrontCenter;
|
||||
|
@ -208,9 +209,10 @@ static void set_from_pulseaudio_channel_map(pa_channel_map channel_map, SoundIoC
|
|||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
static int perform_operation(SoundIo *soundio, pa_operation *op) {
|
||||
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)soundio->backend_data;
|
||||
static int perform_operation(SoundIoPrivate *si, pa_operation *op) {
|
||||
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data;
|
||||
for (;;) {
|
||||
switch (pa_operation_get_state(op)) {
|
||||
case PA_OPERATION_RUNNING:
|
||||
|
@ -226,8 +228,9 @@ static int perform_operation(SoundIo *soundio, pa_operation *op) {
|
|||
}
|
||||
}
|
||||
|
||||
static void finish_device_query(SoundIo *soundio) {
|
||||
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)soundio->backend_data;
|
||||
static void finish_device_query(SoundIoPrivate *si) {
|
||||
SoundIo *soundio = &si->pub;
|
||||
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data;
|
||||
|
||||
if (!sipa->have_sink_list ||
|
||||
!sipa->have_source_list ||
|
||||
|
@ -263,11 +266,12 @@ static void finish_device_query(SoundIo *soundio) {
|
|||
}
|
||||
|
||||
static void sink_info_callback(pa_context *pulse_context, const pa_sink_info *info, int eol, void *userdata) {
|
||||
SoundIo *soundio = (SoundIo *)userdata;
|
||||
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)soundio->backend_data;
|
||||
SoundIoPrivate *si = (SoundIoPrivate *)userdata;
|
||||
SoundIo *soundio = &si->pub;
|
||||
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data;
|
||||
if (eol) {
|
||||
sipa->have_sink_list = true;
|
||||
finish_device_query(soundio);
|
||||
finish_device_query(si);
|
||||
} else {
|
||||
SoundIoDevice *device = create<SoundIoDevice>();
|
||||
if (!device)
|
||||
|
@ -279,8 +283,9 @@ static void sink_info_callback(pa_context *pulse_context, const pa_sink_info *in
|
|||
device->description = strdup(info->description);
|
||||
if (!device->name || !device->description)
|
||||
soundio_panic("out of memory");
|
||||
set_from_pulseaudio_channel_map(info->channel_map, &device->channel_layout);
|
||||
// TODO determine the list of supported formats and the min and max sample rate
|
||||
// TODO determine the channel layouts supported
|
||||
//TODO set_from_pulseaudio_channel_map(info->channel_map, &device->channel_layout);
|
||||
device->current_format = format_from_pulseaudio(info->sample_spec);
|
||||
device->default_latency = usec_to_sec(info->configured_latency);
|
||||
device->sample_rate_current = sample_rate_from_pulseaudio(info->sample_spec);
|
||||
|
@ -293,11 +298,12 @@ static void sink_info_callback(pa_context *pulse_context, const pa_sink_info *in
|
|||
}
|
||||
|
||||
static void source_info_callback(pa_context *pulse_context, const pa_source_info *info, int eol, void *userdata) {
|
||||
SoundIo *soundio = (SoundIo *)userdata;
|
||||
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)soundio->backend_data;
|
||||
SoundIoPrivate *si = (SoundIoPrivate *)userdata;
|
||||
SoundIo *soundio = &si->pub;
|
||||
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data;
|
||||
if (eol) {
|
||||
sipa->have_source_list = true;
|
||||
finish_device_query(soundio);
|
||||
finish_device_query(si);
|
||||
} else {
|
||||
SoundIoDevice *device = create<SoundIoDevice>();
|
||||
if (!device)
|
||||
|
@ -309,8 +315,9 @@ static void source_info_callback(pa_context *pulse_context, const pa_source_info
|
|||
device->description = strdup(info->description);
|
||||
if (!device->name || !device->description)
|
||||
soundio_panic("out of memory");
|
||||
set_from_pulseaudio_channel_map(info->channel_map, &device->channel_layout);
|
||||
// TODO determine the list of supported formats and the min and max sample rate
|
||||
// TODO determine the channel layouts supported
|
||||
// TODO set_from_pulseaudio_channel_map(info->channel_map, &device->channel_layout);
|
||||
device->current_format = format_from_pulseaudio(info->sample_spec);
|
||||
device->default_latency = usec_to_sec(info->configured_latency);
|
||||
device->sample_rate_current = sample_rate_from_pulseaudio(info->sample_spec);
|
||||
|
@ -323,9 +330,9 @@ static void source_info_callback(pa_context *pulse_context, const pa_source_info
|
|||
}
|
||||
|
||||
static void server_info_callback(pa_context *pulse_context, const pa_server_info *info, void *userdata) {
|
||||
SoundIo *soundio = (SoundIo *)userdata;
|
||||
assert(soundio);
|
||||
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)soundio->backend_data;
|
||||
SoundIoPrivate *si = (SoundIoPrivate *)userdata;
|
||||
assert(si);
|
||||
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data;
|
||||
|
||||
free(sipa->default_sink_name);
|
||||
free(sipa->default_source_name);
|
||||
|
@ -337,12 +344,12 @@ static void server_info_callback(pa_context *pulse_context, const pa_server_info
|
|||
soundio_panic("out of memory");
|
||||
|
||||
sipa->have_default_sink = true;
|
||||
finish_device_query(soundio);
|
||||
finish_device_query(si);
|
||||
pa_threaded_mainloop_signal(sipa->main_loop, 0);
|
||||
}
|
||||
|
||||
static void scan_devices(SoundIo *soundio) {
|
||||
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)soundio->backend_data;
|
||||
static void scan_devices(SoundIoPrivate *si) {
|
||||
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data;
|
||||
|
||||
sipa->have_sink_list = false;
|
||||
sipa->have_default_sink = false;
|
||||
|
@ -356,17 +363,17 @@ static void scan_devices(SoundIo *soundio) {
|
|||
pa_threaded_mainloop_lock(sipa->main_loop);
|
||||
|
||||
pa_operation *list_sink_op = pa_context_get_sink_info_list(sipa->pulse_context,
|
||||
sink_info_callback, soundio);
|
||||
sink_info_callback, si);
|
||||
pa_operation *list_source_op = pa_context_get_source_info_list(sipa->pulse_context,
|
||||
source_info_callback, soundio);
|
||||
source_info_callback, si);
|
||||
pa_operation *server_info_op = pa_context_get_server_info(sipa->pulse_context,
|
||||
server_info_callback, soundio);
|
||||
server_info_callback, si);
|
||||
|
||||
if (perform_operation(soundio, list_sink_op))
|
||||
if (perform_operation(si, list_sink_op))
|
||||
soundio_panic("list sinks failed");
|
||||
if (perform_operation(soundio, list_source_op))
|
||||
if (perform_operation(si, list_source_op))
|
||||
soundio_panic("list sources failed");
|
||||
if (perform_operation(soundio, server_info_op))
|
||||
if (perform_operation(si, server_info_op))
|
||||
soundio_panic("get server info failed");
|
||||
|
||||
pa_threaded_mainloop_signal(sipa->main_loop, 0);
|
||||
|
@ -374,8 +381,8 @@ static void scan_devices(SoundIo *soundio) {
|
|||
pa_threaded_mainloop_unlock(sipa->main_loop);
|
||||
}
|
||||
|
||||
static void block_until_have_devices(SoundIo *soundio) {
|
||||
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)soundio->backend_data;
|
||||
static void block_until_have_devices(SoundIoPrivate *si) {
|
||||
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data;
|
||||
if (sipa->have_devices_flag)
|
||||
return;
|
||||
pa_threaded_mainloop_lock(sipa->main_loop);
|
||||
|
@ -385,8 +392,8 @@ static void block_until_have_devices(SoundIo *soundio) {
|
|||
pa_threaded_mainloop_unlock(sipa->main_loop);
|
||||
}
|
||||
|
||||
static void block_until_ready(SoundIo *soundio) {
|
||||
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)soundio->backend_data;
|
||||
static void block_until_ready(SoundIoPrivate *si) {
|
||||
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data;
|
||||
if (sipa->ready_flag)
|
||||
return;
|
||||
pa_threaded_mainloop_lock(sipa->main_loop);
|
||||
|
@ -396,14 +403,15 @@ static void block_until_ready(SoundIo *soundio) {
|
|||
pa_threaded_mainloop_unlock(sipa->main_loop);
|
||||
}
|
||||
|
||||
static void flush_events(SoundIo *soundio) {
|
||||
block_until_ready(soundio);
|
||||
static void flush_events(SoundIoPrivate *si) {
|
||||
SoundIo *soundio = &si->pub;
|
||||
block_until_ready(si);
|
||||
|
||||
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)soundio->backend_data;
|
||||
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data;
|
||||
|
||||
if (sipa->device_scan_queued) {
|
||||
sipa->device_scan_queued = false;
|
||||
scan_devices(soundio);
|
||||
scan_devices(si);
|
||||
}
|
||||
|
||||
SoundIoDevicesInfo *old_devices_info = nullptr;
|
||||
|
@ -412,8 +420,8 @@ static void flush_events(SoundIo *soundio) {
|
|||
pa_threaded_mainloop_lock(sipa->main_loop);
|
||||
|
||||
if (sipa->ready_devices_info) {
|
||||
old_devices_info = soundio->safe_devices_info;
|
||||
soundio->safe_devices_info = sipa->ready_devices_info;
|
||||
old_devices_info = si->safe_devices_info;
|
||||
si->safe_devices_info = sipa->ready_devices_info;
|
||||
sipa->ready_devices_info = nullptr;
|
||||
change = true;
|
||||
}
|
||||
|
@ -425,17 +433,17 @@ static void flush_events(SoundIo *soundio) {
|
|||
|
||||
soundio_destroy_devices_info(old_devices_info);
|
||||
|
||||
block_until_have_devices(soundio);
|
||||
block_until_have_devices(si);
|
||||
}
|
||||
|
||||
static void wait_events(SoundIo *soundio) {
|
||||
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)soundio->backend_data;
|
||||
flush_events(soundio);
|
||||
static void wait_events(SoundIoPrivate *si) {
|
||||
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data;
|
||||
flush_events(si);
|
||||
pa_threaded_mainloop_wait(sipa->main_loop);
|
||||
}
|
||||
|
||||
static void wakeup(SoundIo *soundio) {
|
||||
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)soundio->backend_data;
|
||||
static void wakeup(SoundIoPrivate *si) {
|
||||
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data;
|
||||
pa_threaded_mainloop_signal(sipa->main_loop, 0);
|
||||
}
|
||||
|
||||
|
@ -487,7 +495,6 @@ static pa_channel_position_t to_pulseaudio_channel_pos(SoundIoChannelId channel_
|
|||
case SoundIoChannelIdTopBackCenter: return PA_CHANNEL_POSITION_TOP_REAR_CENTER;
|
||||
case SoundIoChannelIdTopBackRight: return PA_CHANNEL_POSITION_TOP_REAR_RIGHT;
|
||||
|
||||
case SoundIoChannelIdCount:
|
||||
case SoundIoChannelIdInvalid:
|
||||
case SoundIoChannelIdBackLeftCenter:
|
||||
case SoundIoChannelIdBackRightCenter:
|
||||
|
@ -524,10 +531,12 @@ static pa_channel_map to_pulseaudio_channel_map(const SoundIoChannelLayout *chan
|
|||
}
|
||||
|
||||
static void playback_stream_state_callback(pa_stream *stream, void *userdata) {
|
||||
SoundIoOutStream *out_stream = (SoundIoOutStream*) userdata;
|
||||
SoundIo *soundio = out_stream->device->soundio;
|
||||
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)soundio->backend_data;
|
||||
SoundIoOutStreamPulseAudio *ospa = (SoundIoOutStreamPulseAudio *)out_stream->backend_data;
|
||||
SoundIoOutStreamPrivate *os = (SoundIoOutStreamPrivate*) userdata;
|
||||
SoundIoOutStream *outstream = &os->pub;
|
||||
SoundIo *soundio = outstream->device->soundio;
|
||||
SoundIoPrivate *si = (SoundIoPrivate *)soundio;
|
||||
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data;
|
||||
SoundIoOutStreamPulseAudio *ospa = (SoundIoOutStreamPulseAudio *)os->backend_data;
|
||||
switch (pa_stream_get_state(stream)) {
|
||||
case PA_STREAM_UNCONNECTED:
|
||||
case PA_STREAM_CREATING:
|
||||
|
@ -544,25 +553,23 @@ static void playback_stream_state_callback(pa_stream *stream, void *userdata) {
|
|||
}
|
||||
|
||||
static void playback_stream_underflow_callback(pa_stream *stream, void *userdata) {
|
||||
SoundIoOutStream *out_stream = (SoundIoOutStream*)userdata;
|
||||
out_stream->underrun_callback(out_stream);
|
||||
SoundIoOutStream *outstream = (SoundIoOutStream*)userdata;
|
||||
outstream->underrun_callback(outstream);
|
||||
}
|
||||
|
||||
|
||||
static void playback_stream_write_callback(pa_stream *stream, size_t nbytes, void *userdata) {
|
||||
SoundIoOutStream *out_stream = (SoundIoOutStream*)(userdata);
|
||||
int frame_count = ((int)nbytes) / out_stream->bytes_per_frame;
|
||||
out_stream->write_callback(out_stream, frame_count);
|
||||
SoundIoOutStream *outstream = (SoundIoOutStream*)(userdata);
|
||||
int frame_count = ((int)nbytes) / outstream->bytes_per_frame;
|
||||
outstream->write_callback(outstream, frame_count);
|
||||
}
|
||||
|
||||
static void out_stream_destroy_pa(SoundIo *soundio,
|
||||
SoundIoOutStream *out_stream)
|
||||
{
|
||||
SoundIoOutStreamPulseAudio *ospa = (SoundIoOutStreamPulseAudio *)out_stream->backend_data;
|
||||
static void outstream_destroy_pa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os) {
|
||||
SoundIoOutStreamPulseAudio *ospa = (SoundIoOutStreamPulseAudio *)os->backend_data;
|
||||
if (!ospa)
|
||||
return;
|
||||
|
||||
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)soundio->backend_data;
|
||||
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data;
|
||||
pa_stream *stream = ospa->stream;
|
||||
if (stream) {
|
||||
pa_threaded_mainloop_lock(sipa->main_loop);
|
||||
|
@ -580,21 +587,19 @@ static void out_stream_destroy_pa(SoundIo *soundio,
|
|||
}
|
||||
|
||||
destroy(ospa);
|
||||
out_stream->backend_data = nullptr;
|
||||
os->backend_data = nullptr;
|
||||
}
|
||||
|
||||
static int out_stream_init_pa(SoundIo *soundio,
|
||||
SoundIoOutStream *out_stream)
|
||||
{
|
||||
static int outstream_init_pa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os) {
|
||||
SoundIoOutStream *outstream = &os->pub;
|
||||
SoundIoOutStreamPulseAudio *ospa = create<SoundIoOutStreamPulseAudio>();
|
||||
if (!ospa) {
|
||||
out_stream_destroy_pa(soundio, out_stream);
|
||||
outstream_destroy_pa(si, os);
|
||||
return SoundIoErrorNoMem;
|
||||
}
|
||||
out_stream->backend_data = ospa;
|
||||
os->backend_data = ospa;
|
||||
|
||||
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)soundio->backend_data;
|
||||
SoundIoDevice *device = out_stream->device;
|
||||
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data;
|
||||
ospa->stream_ready = false;
|
||||
|
||||
assert(sipa->pulse_context);
|
||||
|
@ -602,24 +607,26 @@ static int out_stream_init_pa(SoundIo *soundio,
|
|||
pa_threaded_mainloop_lock(sipa->main_loop);
|
||||
|
||||
pa_sample_spec sample_spec;
|
||||
sample_spec.format = to_pulseaudio_format(out_stream->format);
|
||||
sample_spec.rate = out_stream->sample_rate;
|
||||
sample_spec.channels = device->channel_layout.channel_count;
|
||||
pa_channel_map channel_map = to_pulseaudio_channel_map(&device->channel_layout);
|
||||
sample_spec.format = to_pulseaudio_format(outstream->format);
|
||||
sample_spec.rate = outstream->sample_rate;
|
||||
|
||||
sample_spec.channels = outstream->layout.channel_count;
|
||||
pa_channel_map channel_map = to_pulseaudio_channel_map(&outstream->layout);
|
||||
|
||||
// TODO make this value ("SoundIo") configurable
|
||||
ospa->stream = pa_stream_new(sipa->pulse_context, "SoundIo", &sample_spec, &channel_map);
|
||||
if (!ospa->stream) {
|
||||
pa_threaded_mainloop_unlock(sipa->main_loop);
|
||||
out_stream_destroy_pa(soundio, out_stream);
|
||||
outstream_destroy_pa(si, os);
|
||||
return SoundIoErrorNoMem;
|
||||
}
|
||||
pa_stream_set_state_callback(ospa->stream, playback_stream_state_callback, out_stream);
|
||||
pa_stream_set_write_callback(ospa->stream, playback_stream_write_callback, out_stream);
|
||||
pa_stream_set_underflow_callback(ospa->stream, playback_stream_underflow_callback, out_stream);
|
||||
pa_stream_set_state_callback(ospa->stream, playback_stream_state_callback, os);
|
||||
pa_stream_set_write_callback(ospa->stream, playback_stream_write_callback, os);
|
||||
pa_stream_set_underflow_callback(ospa->stream, playback_stream_underflow_callback, outstream);
|
||||
|
||||
int bytes_per_second = out_stream->bytes_per_frame * out_stream->sample_rate;
|
||||
int buffer_length = out_stream->bytes_per_frame *
|
||||
ceil(out_stream->latency * bytes_per_second / (double)out_stream->bytes_per_frame);
|
||||
int bytes_per_second = outstream->bytes_per_frame * outstream->sample_rate;
|
||||
int buffer_length = outstream->bytes_per_frame *
|
||||
ceil(outstream->latency * bytes_per_second / (double)outstream->bytes_per_frame);
|
||||
|
||||
ospa->buffer_attr.maxlength = buffer_length;
|
||||
ospa->buffer_attr.tlength = buffer_length;
|
||||
|
@ -632,17 +639,16 @@ static int out_stream_init_pa(SoundIo *soundio,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int out_stream_start_pa(SoundIo *soundio,
|
||||
SoundIoOutStream *out_stream)
|
||||
{
|
||||
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)soundio->backend_data;
|
||||
SoundIoOutStreamPulseAudio *ospa = (SoundIoOutStreamPulseAudio *)out_stream->backend_data;
|
||||
static int outstream_start_pa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os) {
|
||||
SoundIoOutStream *outstream = &os->pub;
|
||||
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data;
|
||||
SoundIoOutStreamPulseAudio *ospa = (SoundIoOutStreamPulseAudio *)os->backend_data;
|
||||
|
||||
pa_threaded_mainloop_lock(sipa->main_loop);
|
||||
|
||||
|
||||
int err = pa_stream_connect_playback(ospa->stream,
|
||||
out_stream->device->name, &ospa->buffer_attr,
|
||||
outstream->device->name, &ospa->buffer_attr,
|
||||
PA_STREAM_ADJUST_LATENCY, nullptr, nullptr);
|
||||
if (err) {
|
||||
pa_threaded_mainloop_unlock(sipa->main_loop);
|
||||
|
@ -652,50 +658,51 @@ static int out_stream_start_pa(SoundIo *soundio,
|
|||
while (!ospa->stream_ready)
|
||||
pa_threaded_mainloop_wait(sipa->main_loop);
|
||||
|
||||
soundio_out_stream_fill_with_silence(out_stream);
|
||||
soundio_outstream_fill_with_silence(outstream);
|
||||
|
||||
pa_threaded_mainloop_unlock(sipa->main_loop);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int out_stream_free_count_pa(SoundIo *soundio,
|
||||
SoundIoOutStream *out_stream)
|
||||
{
|
||||
SoundIoOutStreamPulseAudio *ospa = (SoundIoOutStreamPulseAudio *)out_stream->backend_data;
|
||||
return pa_stream_writable_size(ospa->stream) / out_stream->bytes_per_frame;
|
||||
static int outstream_free_count_pa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os) {
|
||||
SoundIoOutStream *outstream = &os->pub;
|
||||
SoundIoOutStreamPulseAudio *ospa = (SoundIoOutStreamPulseAudio *)os->backend_data;
|
||||
return pa_stream_writable_size(ospa->stream) / outstream->bytes_per_frame;
|
||||
}
|
||||
|
||||
|
||||
static void out_stream_begin_write_pa(SoundIo *soundio,
|
||||
SoundIoOutStream *out_stream, char **data, int *frame_count)
|
||||
static void outstream_begin_write_pa(SoundIoPrivate *si,
|
||||
SoundIoOutStreamPrivate *os, char **data, int *frame_count)
|
||||
{
|
||||
SoundIoOutStreamPulseAudio *ospa = (SoundIoOutStreamPulseAudio *)out_stream->backend_data;
|
||||
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)soundio->backend_data;
|
||||
SoundIoOutStream *outstream = &os->pub;
|
||||
SoundIoOutStreamPulseAudio *ospa = (SoundIoOutStreamPulseAudio *)os->backend_data;
|
||||
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data;
|
||||
pa_stream *stream = ospa->stream;
|
||||
size_t byte_count = *frame_count * out_stream->bytes_per_frame;
|
||||
size_t byte_count = *frame_count * outstream->bytes_per_frame;
|
||||
if (pa_stream_begin_write(stream, (void**)data, &byte_count))
|
||||
soundio_panic("pa_stream_begin_write error: %s", pa_strerror(pa_context_errno(sipa->pulse_context)));
|
||||
|
||||
*frame_count = byte_count / out_stream->bytes_per_frame;
|
||||
*frame_count = byte_count / outstream->bytes_per_frame;
|
||||
}
|
||||
|
||||
static void out_stream_write_pa(SoundIo *soundio,
|
||||
SoundIoOutStream *out_stream, char *data, int frame_count)
|
||||
static void outstream_write_pa(SoundIoPrivate *si,
|
||||
SoundIoOutStreamPrivate *os, char *data, int frame_count)
|
||||
{
|
||||
SoundIoOutStreamPulseAudio *ospa = (SoundIoOutStreamPulseAudio *)out_stream->backend_data;
|
||||
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)soundio->backend_data;
|
||||
SoundIoOutStream *outstream = &os->pub;
|
||||
SoundIoOutStreamPulseAudio *ospa = (SoundIoOutStreamPulseAudio *)os->backend_data;
|
||||
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data;
|
||||
pa_stream *stream = ospa->stream;
|
||||
size_t byte_count = frame_count * out_stream->bytes_per_frame;
|
||||
size_t byte_count = frame_count * outstream->bytes_per_frame;
|
||||
if (pa_stream_write(stream, data, byte_count, NULL, 0, PA_SEEK_RELATIVE))
|
||||
soundio_panic("pa_stream_write error: %s", pa_strerror(pa_context_errno(sipa->pulse_context)));
|
||||
}
|
||||
|
||||
static void out_stream_clear_buffer_pa(SoundIo *soundio,
|
||||
SoundIoOutStream *out_stream)
|
||||
static void outstream_clear_buffer_pa(SoundIoPrivate *si,
|
||||
SoundIoOutStreamPrivate *os)
|
||||
{
|
||||
SoundIoOutStreamPulseAudio *ospa = (SoundIoOutStreamPulseAudio *)out_stream->backend_data;
|
||||
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)soundio->backend_data;
|
||||
SoundIoOutStreamPulseAudio *ospa = (SoundIoOutStreamPulseAudio *)os->backend_data;
|
||||
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data;
|
||||
pa_stream *stream = ospa->stream;
|
||||
pa_threaded_mainloop_lock(sipa->main_loop);
|
||||
pa_operation *op = pa_stream_flush(stream, NULL, NULL);
|
||||
|
@ -706,8 +713,8 @@ static void out_stream_clear_buffer_pa(SoundIo *soundio,
|
|||
}
|
||||
|
||||
static void recording_stream_state_callback(pa_stream *stream, void *userdata) {
|
||||
SoundIoInStream *in_stream = (SoundIoInStream*)userdata;
|
||||
SoundIoInStreamPulseAudio *ispa = (SoundIoInStreamPulseAudio *)in_stream->backend_data;
|
||||
SoundIoInStreamPrivate *is = (SoundIoInStreamPrivate*)userdata;
|
||||
SoundIoInStreamPulseAudio *ispa = (SoundIoInStreamPulseAudio *)is->backend_data;
|
||||
switch (pa_stream_get_state(stream)) {
|
||||
case PA_STREAM_UNCONNECTED:
|
||||
case PA_STREAM_CREATING:
|
||||
|
@ -724,18 +731,17 @@ static void recording_stream_state_callback(pa_stream *stream, void *userdata) {
|
|||
}
|
||||
|
||||
static void recording_stream_read_callback(pa_stream *stream, size_t nbytes, void *userdata) {
|
||||
SoundIoInStream *in_stream = (SoundIoInStream*)userdata;
|
||||
in_stream->read_callback(in_stream);
|
||||
SoundIoInStreamPrivate *is = (SoundIoInStreamPrivate*)userdata;
|
||||
SoundIoInStream *instream = &is->pub;
|
||||
instream->read_callback(instream);
|
||||
}
|
||||
|
||||
static void in_stream_destroy_pa(SoundIo *soundio,
|
||||
SoundIoInStream *in_stream)
|
||||
{
|
||||
SoundIoInStreamPulseAudio *ispa = (SoundIoInStreamPulseAudio *)in_stream->backend_data;
|
||||
static void instream_destroy_pa(SoundIoPrivate *si, SoundIoInStreamPrivate *instream) {
|
||||
SoundIoInStreamPulseAudio *ispa = (SoundIoInStreamPulseAudio *)instream->backend_data;
|
||||
if (!ispa)
|
||||
return;
|
||||
|
||||
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)soundio->backend_data;
|
||||
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data;
|
||||
pa_stream *stream = ispa->stream;
|
||||
if (stream) {
|
||||
pa_threaded_mainloop_lock(sipa->main_loop);
|
||||
|
@ -751,44 +757,43 @@ static void in_stream_destroy_pa(SoundIo *soundio,
|
|||
}
|
||||
}
|
||||
|
||||
static int in_stream_init_pa(SoundIo *soundio,
|
||||
SoundIoInStream *in_stream)
|
||||
{
|
||||
static int instream_init_pa(SoundIoPrivate *si, SoundIoInStreamPrivate *is) {
|
||||
SoundIoInStream *instream = &is->pub;
|
||||
SoundIoInStreamPulseAudio *ispa = create<SoundIoInStreamPulseAudio>();
|
||||
if (!ispa) {
|
||||
in_stream_destroy_pa(soundio, in_stream);
|
||||
instream_destroy_pa(si, is);
|
||||
return SoundIoErrorNoMem;
|
||||
}
|
||||
in_stream->backend_data = ispa;
|
||||
is->backend_data = ispa;
|
||||
|
||||
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)soundio->backend_data;
|
||||
SoundIoDevice *device = in_stream->device;
|
||||
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data;
|
||||
ispa->stream_ready = false;
|
||||
|
||||
pa_threaded_mainloop_lock(sipa->main_loop);
|
||||
|
||||
pa_sample_spec sample_spec;
|
||||
sample_spec.format = to_pulseaudio_format(in_stream->format);
|
||||
sample_spec.rate = in_stream->sample_rate;
|
||||
sample_spec.channels = device->channel_layout.channel_count;
|
||||
sample_spec.format = to_pulseaudio_format(instream->format);
|
||||
sample_spec.rate = instream->sample_rate;
|
||||
sample_spec.channels = instream->layout.channel_count;
|
||||
|
||||
pa_channel_map channel_map = to_pulseaudio_channel_map(&device->channel_layout);
|
||||
pa_channel_map channel_map = to_pulseaudio_channel_map(&instream->layout);
|
||||
|
||||
// TODO make this value ("SoundIo") private
|
||||
ispa->stream = pa_stream_new(sipa->pulse_context, "SoundIo", &sample_spec, &channel_map);
|
||||
if (!in_stream) {
|
||||
if (!ispa->stream) {
|
||||
pa_threaded_mainloop_unlock(sipa->main_loop);
|
||||
in_stream_destroy_pa(soundio, in_stream);
|
||||
instream_destroy_pa(si, is);
|
||||
return SoundIoErrorNoMem;
|
||||
}
|
||||
|
||||
pa_stream *stream = ispa->stream;
|
||||
|
||||
pa_stream_set_state_callback(stream, recording_stream_state_callback, in_stream);
|
||||
pa_stream_set_read_callback(stream, recording_stream_read_callback, in_stream);
|
||||
pa_stream_set_state_callback(stream, recording_stream_state_callback, is);
|
||||
pa_stream_set_read_callback(stream, recording_stream_read_callback, is);
|
||||
|
||||
int bytes_per_second = in_stream->bytes_per_frame * in_stream->sample_rate;
|
||||
int buffer_length = in_stream->bytes_per_frame *
|
||||
ceil(in_stream->latency * bytes_per_second / (double)in_stream->bytes_per_frame);
|
||||
int bytes_per_second = instream->bytes_per_frame * instream->sample_rate;
|
||||
int buffer_length = instream->bytes_per_frame *
|
||||
ceil(instream->latency * bytes_per_second / (double)instream->bytes_per_frame);
|
||||
|
||||
ispa->buffer_attr.maxlength = UINT32_MAX;
|
||||
ispa->buffer_attr.tlength = UINT32_MAX;
|
||||
|
@ -801,15 +806,14 @@ static int in_stream_init_pa(SoundIo *soundio,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int in_stream_start_pa(SoundIo *soundio,
|
||||
SoundIoInStream *in_stream)
|
||||
{
|
||||
SoundIoInStreamPulseAudio *ispa = (SoundIoInStreamPulseAudio *)in_stream->backend_data;
|
||||
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)soundio->backend_data;
|
||||
static int instream_start_pa(SoundIoPrivate *si, SoundIoInStreamPrivate *is) {
|
||||
SoundIoInStream *instream = &is->pub;
|
||||
SoundIoInStreamPulseAudio *ispa = (SoundIoInStreamPulseAudio *)is->backend_data;
|
||||
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data;
|
||||
pa_threaded_mainloop_lock(sipa->main_loop);
|
||||
|
||||
int err = pa_stream_connect_record(ispa->stream,
|
||||
in_stream->device->name,
|
||||
instream->device->name,
|
||||
&ispa->buffer_attr, PA_STREAM_ADJUST_LATENCY);
|
||||
if (err) {
|
||||
pa_threaded_mainloop_unlock(sipa->main_loop);
|
||||
|
@ -820,40 +824,37 @@ static int in_stream_start_pa(SoundIo *soundio,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void in_stream_peek_pa(SoundIo *soundio,
|
||||
SoundIoInStream *in_stream, const char **data, int *frame_count)
|
||||
static void instream_peek_pa(SoundIoPrivate *si,
|
||||
SoundIoInStreamPrivate *is, const char **data, int *frame_count)
|
||||
{
|
||||
SoundIoInStreamPulseAudio *ispa = (SoundIoInStreamPulseAudio *)in_stream->backend_data;
|
||||
SoundIoInStream *instream = &is->pub;
|
||||
SoundIoInStreamPulseAudio *ispa = (SoundIoInStreamPulseAudio *)is->backend_data;
|
||||
pa_stream *stream = ispa->stream;
|
||||
if (ispa->stream_ready) {
|
||||
size_t nbytes;
|
||||
if (pa_stream_peek(stream, (const void **)data, &nbytes))
|
||||
soundio_panic("pa_stream_peek error: %s", pa_strerror(pa_context_errno(pa_stream_get_context(stream))));
|
||||
*frame_count = ((int)nbytes) / in_stream->bytes_per_frame;
|
||||
*frame_count = ((int)nbytes) / instream->bytes_per_frame;
|
||||
} else {
|
||||
*data = nullptr;
|
||||
*frame_count = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void in_stream_drop_pa(SoundIo *soundio,
|
||||
SoundIoInStream *in_stream)
|
||||
{
|
||||
SoundIoInStreamPulseAudio *ispa = (SoundIoInStreamPulseAudio *)in_stream->backend_data;
|
||||
static void instream_drop_pa(SoundIoPrivate *si, SoundIoInStreamPrivate *is) {
|
||||
SoundIoInStreamPulseAudio *ispa = (SoundIoInStreamPulseAudio *)is->backend_data;
|
||||
pa_stream *stream = ispa->stream;
|
||||
if (pa_stream_drop(stream))
|
||||
soundio_panic("pa_stream_drop error: %s", pa_strerror(pa_context_errno(pa_stream_get_context(stream))));
|
||||
}
|
||||
|
||||
static void in_stream_clear_buffer_pa(SoundIo *soundio,
|
||||
SoundIoInStream *in_stream)
|
||||
{
|
||||
SoundIoInStreamPulseAudio *ispa = (SoundIoInStreamPulseAudio *)in_stream->backend_data;
|
||||
static void instream_clear_buffer_pa(SoundIoPrivate *si, SoundIoInStreamPrivate *is) {
|
||||
SoundIoInStreamPulseAudio *ispa = (SoundIoInStreamPulseAudio *)is->backend_data;
|
||||
if (!ispa->stream_ready)
|
||||
return;
|
||||
|
||||
pa_stream *stream = ispa->stream;
|
||||
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)soundio->backend_data;
|
||||
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data;
|
||||
|
||||
pa_threaded_mainloop_lock(sipa->main_loop);
|
||||
|
||||
|
@ -873,14 +874,14 @@ static void in_stream_clear_buffer_pa(SoundIo *soundio,
|
|||
pa_threaded_mainloop_unlock(sipa->main_loop);
|
||||
}
|
||||
|
||||
int soundio_pulseaudio_init(SoundIo *soundio) {
|
||||
assert(!soundio->backend_data);
|
||||
int soundio_pulseaudio_init(SoundIoPrivate *si) {
|
||||
assert(!si->backend_data);
|
||||
SoundIoPulseAudio *sipa = create<SoundIoPulseAudio>();
|
||||
if (!sipa) {
|
||||
destroy_pa(soundio);
|
||||
destroy_pa(si);
|
||||
return SoundIoErrorNoMem;
|
||||
}
|
||||
soundio->backend_data = sipa;
|
||||
si->backend_data = sipa;
|
||||
|
||||
sipa->connection_refused = false;
|
||||
sipa->device_scan_queued = false;
|
||||
|
@ -889,7 +890,7 @@ int soundio_pulseaudio_init(SoundIo *soundio) {
|
|||
|
||||
sipa->main_loop = pa_threaded_mainloop_new();
|
||||
if (!sipa->main_loop) {
|
||||
destroy_pa(soundio);
|
||||
destroy_pa(si);
|
||||
return SoundIoErrorNoMem;
|
||||
}
|
||||
|
||||
|
@ -897,7 +898,7 @@ int soundio_pulseaudio_init(SoundIo *soundio) {
|
|||
|
||||
sipa->props = pa_proplist_new();
|
||||
if (!sipa->props) {
|
||||
destroy_pa(soundio);
|
||||
destroy_pa(si);
|
||||
return SoundIoErrorNoMem;
|
||||
}
|
||||
|
||||
|
@ -908,48 +909,48 @@ int soundio_pulseaudio_init(SoundIo *soundio) {
|
|||
|
||||
sipa->pulse_context = pa_context_new_with_proplist(main_loop_api, "SoundIo", sipa->props);
|
||||
if (!sipa->pulse_context) {
|
||||
destroy_pa(soundio);
|
||||
destroy_pa(si);
|
||||
return SoundIoErrorNoMem;
|
||||
}
|
||||
|
||||
pa_context_set_subscribe_callback(sipa->pulse_context, subscribe_callback, soundio);
|
||||
pa_context_set_state_callback(sipa->pulse_context, context_state_callback, soundio);
|
||||
pa_context_set_subscribe_callback(sipa->pulse_context, subscribe_callback, si);
|
||||
pa_context_set_state_callback(sipa->pulse_context, context_state_callback, si);
|
||||
|
||||
int err = pa_context_connect(sipa->pulse_context, NULL, (pa_context_flags_t)0, NULL);
|
||||
if (err) {
|
||||
destroy_pa(soundio);
|
||||
destroy_pa(si);
|
||||
return SoundIoErrorInitAudioBackend;
|
||||
}
|
||||
|
||||
if (sipa->connection_refused) {
|
||||
destroy_pa(soundio);
|
||||
destroy_pa(si);
|
||||
return SoundIoErrorInitAudioBackend;
|
||||
}
|
||||
|
||||
if (pa_threaded_mainloop_start(sipa->main_loop)) {
|
||||
destroy_pa(soundio);
|
||||
destroy_pa(si);
|
||||
return SoundIoErrorNoMem;
|
||||
}
|
||||
|
||||
soundio->destroy = destroy_pa;
|
||||
soundio->flush_events = flush_events;
|
||||
soundio->wait_events = wait_events;
|
||||
soundio->wakeup = wakeup;
|
||||
si->destroy = destroy_pa;
|
||||
si->flush_events = flush_events;
|
||||
si->wait_events = wait_events;
|
||||
si->wakeup = wakeup;
|
||||
|
||||
soundio->out_stream_init = out_stream_init_pa;
|
||||
soundio->out_stream_destroy = out_stream_destroy_pa;
|
||||
soundio->out_stream_start = out_stream_start_pa;
|
||||
soundio->out_stream_free_count = out_stream_free_count_pa;
|
||||
soundio->out_stream_begin_write = out_stream_begin_write_pa;
|
||||
soundio->out_stream_write = out_stream_write_pa;
|
||||
soundio->out_stream_clear_buffer = out_stream_clear_buffer_pa;
|
||||
si->outstream_init = outstream_init_pa;
|
||||
si->outstream_destroy = outstream_destroy_pa;
|
||||
si->outstream_start = outstream_start_pa;
|
||||
si->outstream_free_count = outstream_free_count_pa;
|
||||
si->outstream_begin_write = outstream_begin_write_pa;
|
||||
si->outstream_write = outstream_write_pa;
|
||||
si->outstream_clear_buffer = outstream_clear_buffer_pa;
|
||||
|
||||
soundio->in_stream_init = in_stream_init_pa;
|
||||
soundio->in_stream_destroy = in_stream_destroy_pa;
|
||||
soundio->in_stream_start = in_stream_start_pa;
|
||||
soundio->in_stream_peek = in_stream_peek_pa;
|
||||
soundio->in_stream_drop = in_stream_drop_pa;
|
||||
soundio->in_stream_clear_buffer = in_stream_clear_buffer_pa;
|
||||
si->instream_init = instream_init_pa;
|
||||
si->instream_destroy = instream_destroy_pa;
|
||||
si->instream_start = instream_start_pa;
|
||||
si->instream_peek = instream_peek_pa;
|
||||
si->instream_drop = instream_drop_pa;
|
||||
si->instream_clear_buffer = instream_clear_buffer_pa;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -8,6 +8,6 @@
|
|||
#ifndef SOUNDIO_PULSEAUDIO_HPP
|
||||
#define SOUNDIO_PULSEAUDIO_HPP
|
||||
|
||||
int soundio_pulseaudio_init(struct SoundIo *soundio);
|
||||
int soundio_pulseaudio_init(struct SoundIoPrivate *si);
|
||||
|
||||
#endif
|
||||
|
|
441
src/soundio.cpp
441
src/soundio.cpp
|
@ -22,6 +22,16 @@
|
|||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
static const SoundIoBackend available_backends[] = {
|
||||
#ifdef SOUNDIO_HAVE_PULSEAUDIO
|
||||
SoundIoBackendPulseAudio,
|
||||
#endif
|
||||
#ifdef SOUNDIO_HAVE_ALSA
|
||||
SoundIoBackendAlsa,
|
||||
#endif
|
||||
SoundIoBackendDummy,
|
||||
};
|
||||
|
||||
const char *soundio_strerror(int error) {
|
||||
switch ((enum SoundIoError)error) {
|
||||
case SoundIoErrorNone: return "(no error)";
|
||||
|
@ -29,6 +39,8 @@ const char *soundio_strerror(int error) {
|
|||
case SoundIoErrorInitAudioBackend: return "unable to initialize audio backend";
|
||||
case SoundIoErrorSystemResources: return "system resource not available";
|
||||
case SoundIoErrorOpeningDevice: return "unable to open device";
|
||||
case SoundIoErrorInvalid: return "invalid value";
|
||||
case SoundIoErrorBackendUnavailable: return "backend unavailable";
|
||||
}
|
||||
soundio_panic("invalid error enum value: %d", error);
|
||||
}
|
||||
|
@ -99,12 +111,13 @@ const char *soundio_backend_name(enum SoundIoBackend backend) {
|
|||
}
|
||||
|
||||
void soundio_destroy(struct SoundIo *soundio) {
|
||||
if (!soundio)
|
||||
SoundIoPrivate *si = (SoundIoPrivate *)soundio;
|
||||
if (!si)
|
||||
return;
|
||||
|
||||
soundio_disconnect(soundio);
|
||||
|
||||
destroy(soundio);
|
||||
destroy(si);
|
||||
}
|
||||
|
||||
static void default_on_devices_change(struct SoundIo *) { }
|
||||
|
@ -112,126 +125,156 @@ static void default_on_events_signal(struct SoundIo *) { }
|
|||
|
||||
struct SoundIo * soundio_create(void) {
|
||||
soundio_os_init();
|
||||
struct SoundIo *soundio = create<SoundIo>();
|
||||
if (!soundio) {
|
||||
soundio_destroy(soundio);
|
||||
struct SoundIoPrivate *si = create<SoundIoPrivate>();
|
||||
if (!si)
|
||||
return NULL;
|
||||
}
|
||||
SoundIo *soundio = &si->pub;
|
||||
soundio->on_devices_change = default_on_devices_change;
|
||||
soundio->on_events_signal = default_on_events_signal;
|
||||
return soundio;
|
||||
}
|
||||
|
||||
int soundio_connect(struct SoundIo *soundio) {
|
||||
int err = 0;
|
||||
|
||||
for (int i = 0; i < array_length(available_backends); i += 1) {
|
||||
SoundIoBackend backend = available_backends[i];
|
||||
err = soundio_connect_backend(soundio, backend);
|
||||
if (!err)
|
||||
return 0;
|
||||
if (err != SoundIoErrorInitAudioBackend)
|
||||
return err;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int soundio_connect_backend(SoundIo *soundio, SoundIoBackend backend) {
|
||||
SoundIoPrivate *si = (SoundIoPrivate *)soundio;
|
||||
|
||||
if (si->current_backend)
|
||||
return SoundIoErrorInvalid;
|
||||
|
||||
int err;
|
||||
|
||||
switch (backend) {
|
||||
case SoundIoBackendPulseAudio:
|
||||
#ifdef SOUNDIO_HAVE_PULSEAUDIO
|
||||
soundio->current_backend = SoundIoBackendPulseAudio;
|
||||
err = soundio_pulseaudio_init(soundio);
|
||||
if (!err)
|
||||
si->current_backend = SoundIoBackendPulseAudio;
|
||||
if ((err = soundio_pulseaudio_init(si))) {
|
||||
soundio_disconnect(soundio);
|
||||
return err;
|
||||
}
|
||||
return 0;
|
||||
if (err != SoundIoErrorInitAudioBackend) {
|
||||
soundio_disconnect(soundio);
|
||||
return err;
|
||||
}
|
||||
#else
|
||||
return SoundIoErrorBackendUnavailable;
|
||||
#endif
|
||||
|
||||
case SoundIoBackendAlsa:
|
||||
#ifdef SOUNDIO_HAVE_ALSA
|
||||
soundio->current_backend = SoundIoBackendAlsa;
|
||||
err = soundio_alsa_init(soundio);
|
||||
if (!err)
|
||||
si->current_backend = SoundIoBackendAlsa;
|
||||
if ((err = soundio_alsa_init(si))) {
|
||||
soundio_disconnect(soundio);
|
||||
return err;
|
||||
}
|
||||
return 0;
|
||||
if (err != SoundIoErrorInitAudioBackend) {
|
||||
soundio_disconnect(soundio);
|
||||
return err;
|
||||
}
|
||||
#else
|
||||
return SoundIoErrorBackendUnavailable;
|
||||
#endif
|
||||
|
||||
soundio->current_backend = SoundIoBackendDummy;
|
||||
err = soundio_dummy_init(soundio);
|
||||
if (err) {
|
||||
soundio_disconnect(soundio);
|
||||
case SoundIoBackendDummy:
|
||||
si->current_backend = SoundIoBackendDummy;
|
||||
err = soundio_dummy_init(si);
|
||||
if (err)
|
||||
soundio_disconnect(soundio);
|
||||
return err;
|
||||
case SoundIoBackendNone:
|
||||
return SoundIoErrorInvalid;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return SoundIoErrorInvalid;
|
||||
}
|
||||
|
||||
void soundio_disconnect(struct SoundIo *soundio) {
|
||||
if (soundio->destroy)
|
||||
soundio->destroy(soundio);
|
||||
assert(!soundio->backend_data);
|
||||
SoundIoPrivate *si = (SoundIoPrivate *)soundio;
|
||||
|
||||
soundio->current_backend = SoundIoBackendNone;
|
||||
if (si->destroy)
|
||||
si->destroy(si);
|
||||
assert(!si->backend_data);
|
||||
|
||||
soundio_destroy_devices_info(soundio->safe_devices_info);
|
||||
soundio->safe_devices_info = nullptr;
|
||||
si->current_backend = SoundIoBackendNone;
|
||||
|
||||
soundio->destroy = nullptr;
|
||||
soundio->flush_events = nullptr;
|
||||
soundio->wait_events = nullptr;
|
||||
soundio->wakeup = nullptr;
|
||||
soundio_destroy_devices_info(si->safe_devices_info);
|
||||
si->safe_devices_info = nullptr;
|
||||
|
||||
soundio->out_stream_init = nullptr;
|
||||
soundio->out_stream_destroy = nullptr;
|
||||
soundio->out_stream_start = nullptr;
|
||||
soundio->out_stream_free_count = nullptr;
|
||||
soundio->out_stream_begin_write = nullptr;
|
||||
soundio->out_stream_write = nullptr;
|
||||
soundio->out_stream_clear_buffer = nullptr;
|
||||
si->destroy = nullptr;
|
||||
si->flush_events = nullptr;
|
||||
si->wait_events = nullptr;
|
||||
si->wakeup = nullptr;
|
||||
|
||||
soundio->in_stream_init = nullptr;
|
||||
soundio->in_stream_destroy = nullptr;
|
||||
soundio->in_stream_start = nullptr;
|
||||
soundio->in_stream_peek = nullptr;
|
||||
soundio->in_stream_drop = nullptr;
|
||||
soundio->in_stream_clear_buffer = nullptr;
|
||||
si->outstream_init = nullptr;
|
||||
si->outstream_destroy = nullptr;
|
||||
si->outstream_start = nullptr;
|
||||
si->outstream_free_count = nullptr;
|
||||
si->outstream_begin_write = nullptr;
|
||||
si->outstream_write = nullptr;
|
||||
si->outstream_clear_buffer = nullptr;
|
||||
|
||||
si->instream_init = nullptr;
|
||||
si->instream_destroy = nullptr;
|
||||
si->instream_start = nullptr;
|
||||
si->instream_peek = nullptr;
|
||||
si->instream_drop = nullptr;
|
||||
si->instream_clear_buffer = nullptr;
|
||||
}
|
||||
|
||||
void soundio_flush_events(struct SoundIo *soundio) {
|
||||
assert(soundio->flush_events);
|
||||
if (soundio->flush_events)
|
||||
soundio->flush_events(soundio);
|
||||
SoundIoPrivate *si = (SoundIoPrivate *)soundio;
|
||||
assert(si->flush_events);
|
||||
if (si->flush_events)
|
||||
si->flush_events(si);
|
||||
}
|
||||
|
||||
int soundio_get_input_device_count(struct SoundIo *soundio) {
|
||||
SoundIoPrivate *si = (SoundIoPrivate *)soundio;
|
||||
soundio_flush_events(soundio);
|
||||
assert(soundio->safe_devices_info);
|
||||
return soundio->safe_devices_info->input_devices.length;
|
||||
assert(si->safe_devices_info);
|
||||
return si->safe_devices_info->input_devices.length;
|
||||
}
|
||||
|
||||
int soundio_get_output_device_count(struct SoundIo *soundio) {
|
||||
SoundIoPrivate *si = (SoundIoPrivate *)soundio;
|
||||
soundio_flush_events(soundio);
|
||||
assert(soundio->safe_devices_info);
|
||||
return soundio->safe_devices_info->output_devices.length;
|
||||
assert(si->safe_devices_info);
|
||||
return si->safe_devices_info->output_devices.length;
|
||||
}
|
||||
|
||||
int soundio_get_default_input_device_index(struct SoundIo *soundio) {
|
||||
SoundIoPrivate *si = (SoundIoPrivate *)soundio;
|
||||
soundio_flush_events(soundio);
|
||||
assert(soundio->safe_devices_info);
|
||||
return soundio->safe_devices_info->default_input_index;
|
||||
assert(si->safe_devices_info);
|
||||
return si->safe_devices_info->default_input_index;
|
||||
}
|
||||
|
||||
int soundio_get_default_output_device_index(struct SoundIo *soundio) {
|
||||
SoundIoPrivate *si = (SoundIoPrivate *)soundio;
|
||||
soundio_flush_events(soundio);
|
||||
assert(soundio->safe_devices_info);
|
||||
return soundio->safe_devices_info->default_output_index;
|
||||
assert(si->safe_devices_info);
|
||||
return si->safe_devices_info->default_output_index;
|
||||
}
|
||||
|
||||
struct SoundIoDevice *soundio_get_input_device(struct SoundIo *soundio, int index) {
|
||||
assert(soundio->safe_devices_info);
|
||||
SoundIoPrivate *si = (SoundIoPrivate *)soundio;
|
||||
assert(si->safe_devices_info);
|
||||
assert(index >= 0);
|
||||
assert(index < soundio->safe_devices_info->input_devices.length);
|
||||
SoundIoDevice *device = soundio->safe_devices_info->input_devices.at(index);
|
||||
assert(index < si->safe_devices_info->input_devices.length);
|
||||
SoundIoDevice *device = si->safe_devices_info->input_devices.at(index);
|
||||
soundio_device_ref(device);
|
||||
return device;
|
||||
}
|
||||
|
||||
struct SoundIoDevice *soundio_get_output_device(struct SoundIo *soundio, int index) {
|
||||
assert(soundio->safe_devices_info);
|
||||
SoundIoPrivate *si = (SoundIoPrivate *)soundio;
|
||||
assert(si->safe_devices_info);
|
||||
assert(index >= 0);
|
||||
assert(index < soundio->safe_devices_info->output_devices.length);
|
||||
SoundIoDevice *device = soundio->safe_devices_info->output_devices.at(index);
|
||||
assert(index < si->safe_devices_info->output_devices.length);
|
||||
SoundIoDevice *device = si->safe_devices_info->output_devices.at(index);
|
||||
soundio_device_ref(device);
|
||||
return device;
|
||||
}
|
||||
|
@ -250,10 +293,6 @@ enum SoundIoDevicePurpose soundio_device_purpose(const struct SoundIoDevice *dev
|
|||
return device->purpose;
|
||||
}
|
||||
|
||||
const struct SoundIoChannelLayout *soundio_device_channel_layout(const struct SoundIoDevice *device) {
|
||||
return &device->channel_layout;
|
||||
}
|
||||
|
||||
void soundio_device_unref(struct SoundIoDevice *device) {
|
||||
if (!device)
|
||||
return;
|
||||
|
@ -274,151 +313,140 @@ void soundio_device_ref(struct SoundIoDevice *device) {
|
|||
}
|
||||
|
||||
void soundio_wait_events(struct SoundIo *soundio) {
|
||||
soundio->wait_events(soundio);
|
||||
SoundIoPrivate *si = (SoundIoPrivate *)soundio;
|
||||
si->wait_events(si);
|
||||
}
|
||||
|
||||
void soundio_wakeup(struct SoundIo *soundio) {
|
||||
soundio->wakeup(soundio);
|
||||
SoundIoPrivate *si = (SoundIoPrivate *)soundio;
|
||||
si->wakeup(si);
|
||||
}
|
||||
|
||||
void soundio_out_stream_fill_with_silence(struct SoundIoOutStream *out_stream) {
|
||||
void soundio_outstream_fill_with_silence(struct SoundIoOutStream *outstream) {
|
||||
char *buffer;
|
||||
int requested_frame_count = soundio_out_stream_free_count(out_stream);
|
||||
int requested_frame_count = soundio_outstream_free_count(outstream);
|
||||
while (requested_frame_count > 0) {
|
||||
int frame_count = requested_frame_count;
|
||||
soundio_out_stream_begin_write(out_stream, &buffer, &frame_count);
|
||||
memset(buffer, 0, frame_count * out_stream->bytes_per_frame);
|
||||
soundio_out_stream_write(out_stream, buffer, frame_count);
|
||||
soundio_outstream_begin_write(outstream, &buffer, &frame_count);
|
||||
memset(buffer, 0, frame_count * outstream->bytes_per_frame);
|
||||
soundio_outstream_write(outstream, buffer, frame_count);
|
||||
requested_frame_count -= frame_count;
|
||||
}
|
||||
}
|
||||
|
||||
int soundio_out_stream_free_count(struct SoundIoOutStream *out_stream) {
|
||||
SoundIo *soundio = out_stream->device->soundio;
|
||||
return soundio->out_stream_free_count(soundio, out_stream);
|
||||
int soundio_outstream_free_count(struct SoundIoOutStream *outstream) {
|
||||
SoundIo *soundio = outstream->device->soundio;
|
||||
SoundIoPrivate *si = (SoundIoPrivate *)soundio;
|
||||
SoundIoOutStreamPrivate *os = (SoundIoOutStreamPrivate *)outstream;
|
||||
return si->outstream_free_count(si, os);
|
||||
}
|
||||
|
||||
void soundio_out_stream_begin_write(struct SoundIoOutStream *out_stream,
|
||||
void soundio_outstream_begin_write(struct SoundIoOutStream *outstream,
|
||||
char **data, int *frame_count)
|
||||
{
|
||||
SoundIo *soundio = out_stream->device->soundio;
|
||||
soundio->out_stream_begin_write(soundio, out_stream, data, frame_count);
|
||||
SoundIo *soundio = outstream->device->soundio;
|
||||
SoundIoPrivate *si = (SoundIoPrivate *)soundio;
|
||||
SoundIoOutStreamPrivate *os = (SoundIoOutStreamPrivate *)outstream;
|
||||
si->outstream_begin_write(si, os, data, frame_count);
|
||||
}
|
||||
|
||||
void soundio_out_stream_write(struct SoundIoOutStream *out_stream,
|
||||
void soundio_outstream_write(struct SoundIoOutStream *outstream,
|
||||
char *data, int frame_count)
|
||||
{
|
||||
SoundIo *soundio = out_stream->device->soundio;
|
||||
soundio->out_stream_write(soundio, out_stream, data, frame_count);
|
||||
SoundIo *soundio = outstream->device->soundio;
|
||||
SoundIoPrivate *si = (SoundIoPrivate *)soundio;
|
||||
SoundIoOutStreamPrivate *os = (SoundIoOutStreamPrivate *)outstream;
|
||||
si->outstream_write(si, os, data, frame_count);
|
||||
}
|
||||
|
||||
|
||||
int soundio_out_stream_create(struct SoundIoDevice *device,
|
||||
enum SoundIoFormat format, int sample_rate,
|
||||
double latency, void *userdata,
|
||||
void (*write_callback)(struct SoundIoOutStream *, int frame_count),
|
||||
void (*underrun_callback)(struct SoundIoOutStream *),
|
||||
struct SoundIoOutStream **out_out_stream)
|
||||
{
|
||||
*out_out_stream = nullptr;
|
||||
|
||||
SoundIoOutStream *out_stream = create<SoundIoOutStream>();
|
||||
if (!out_stream) {
|
||||
soundio_out_stream_destroy(out_stream);
|
||||
return SoundIoErrorNoMem;
|
||||
}
|
||||
struct SoundIoOutStream *soundio_outstream_create(struct SoundIoDevice *device) {
|
||||
SoundIoOutStreamPrivate *os = create<SoundIoOutStreamPrivate>();
|
||||
if (!os)
|
||||
return nullptr;
|
||||
SoundIoOutStream *outstream = &os->pub;
|
||||
|
||||
outstream->device = device;
|
||||
soundio_device_ref(device);
|
||||
out_stream->device = device;
|
||||
out_stream->userdata = userdata;
|
||||
out_stream->write_callback = write_callback;
|
||||
out_stream->underrun_callback = underrun_callback;
|
||||
out_stream->format = format;
|
||||
out_stream->sample_rate = sample_rate;
|
||||
out_stream->latency = latency;
|
||||
out_stream->bytes_per_frame = soundio_get_bytes_per_frame(format,
|
||||
device->channel_layout.channel_count);
|
||||
|
||||
SoundIo *soundio = device->soundio;
|
||||
int err = soundio->out_stream_init(soundio, out_stream);
|
||||
if (err) {
|
||||
soundio_out_stream_destroy(out_stream);
|
||||
return err;
|
||||
}
|
||||
// TODO set defaults
|
||||
|
||||
*out_out_stream = out_stream;
|
||||
return 0;
|
||||
return outstream;
|
||||
}
|
||||
|
||||
void soundio_out_stream_destroy(SoundIoOutStream *out_stream) {
|
||||
if (!out_stream)
|
||||
int soundio_outstream_open(struct SoundIoOutStream *outstream) {
|
||||
SoundIoOutStreamPrivate *os = (SoundIoOutStreamPrivate *)outstream;
|
||||
outstream->bytes_per_frame = soundio_get_bytes_per_frame(outstream->format, outstream->layout.channel_count);
|
||||
|
||||
SoundIo *soundio = outstream->device->soundio;
|
||||
SoundIoPrivate *si = (SoundIoPrivate *)soundio;
|
||||
return si->outstream_init(si, os);
|
||||
}
|
||||
|
||||
void soundio_outstream_destroy(SoundIoOutStream *outstream) {
|
||||
if (!outstream)
|
||||
return;
|
||||
|
||||
SoundIo *soundio = out_stream->device->soundio;
|
||||
SoundIoOutStreamPrivate *os = (SoundIoOutStreamPrivate *)outstream;
|
||||
SoundIo *soundio = outstream->device->soundio;
|
||||
SoundIoPrivate *si = (SoundIoPrivate *)soundio;
|
||||
|
||||
if (soundio->out_stream_destroy)
|
||||
soundio->out_stream_destroy(soundio, out_stream);
|
||||
if (si->outstream_destroy)
|
||||
si->outstream_destroy(si, os);
|
||||
|
||||
soundio_device_unref(out_stream->device);
|
||||
destroy(out_stream);
|
||||
soundio_device_unref(outstream->device);
|
||||
destroy(os);
|
||||
}
|
||||
|
||||
int soundio_out_stream_start(struct SoundIoOutStream *out_stream) {
|
||||
SoundIo *soundio = out_stream->device->soundio;
|
||||
return soundio->out_stream_start(soundio, out_stream);
|
||||
int soundio_outstream_start(struct SoundIoOutStream *outstream) {
|
||||
SoundIo *soundio = outstream->device->soundio;
|
||||
SoundIoPrivate *si = (SoundIoPrivate *)soundio;
|
||||
SoundIoOutStreamPrivate *os = (SoundIoOutStreamPrivate *)outstream;
|
||||
return si->outstream_start(si, os);
|
||||
}
|
||||
|
||||
int soundio_in_stream_create(struct SoundIoDevice *device,
|
||||
enum SoundIoFormat format, int sample_rate,
|
||||
double latency, void *userdata,
|
||||
void (*read_callback)(struct SoundIoInStream *),
|
||||
struct SoundIoInStream **out_in_stream)
|
||||
{
|
||||
*out_in_stream = nullptr;
|
||||
|
||||
SoundIoInStream *sid = create<SoundIoInStream>();
|
||||
if (!sid) {
|
||||
soundio_in_stream_destroy(sid);
|
||||
return SoundIoErrorNoMem;
|
||||
}
|
||||
struct SoundIoInStream *soundio_instream_create(struct SoundIoDevice *device) {
|
||||
SoundIoInStreamPrivate *is = create<SoundIoInStreamPrivate>();
|
||||
if (!is)
|
||||
return nullptr;
|
||||
SoundIoInStream *instream = &is->pub;
|
||||
|
||||
instream->device = device;
|
||||
soundio_device_ref(device);
|
||||
sid->device = device;
|
||||
sid->userdata = userdata;
|
||||
sid->read_callback = read_callback;
|
||||
sid->format = format;
|
||||
sid->latency = latency;
|
||||
sid->sample_rate = sample_rate;
|
||||
sid->bytes_per_frame = soundio_get_bytes_per_frame(format,
|
||||
device->channel_layout.channel_count);
|
||||
|
||||
SoundIo *soundio = device->soundio;
|
||||
int err = soundio->in_stream_init(soundio, sid);
|
||||
if (err) {
|
||||
soundio_in_stream_destroy(sid);
|
||||
return err;
|
||||
}
|
||||
// TODO set defaults
|
||||
|
||||
*out_in_stream = sid;
|
||||
return 0;
|
||||
return instream;
|
||||
}
|
||||
|
||||
int soundio_in_stream_start(struct SoundIoInStream *in_stream) {
|
||||
SoundIo *soundio = in_stream->device->soundio;
|
||||
return soundio->in_stream_start(soundio, in_stream);
|
||||
int soundio_instream_open(struct SoundIoInStream *instream) {
|
||||
instream->bytes_per_frame = soundio_get_bytes_per_frame(instream->format, instream->layout.channel_count);
|
||||
SoundIo *soundio = instream->device->soundio;
|
||||
SoundIoPrivate *si = (SoundIoPrivate *)soundio;
|
||||
SoundIoInStreamPrivate *is = (SoundIoInStreamPrivate *)instream;
|
||||
return si->instream_init(si, is);
|
||||
}
|
||||
|
||||
void soundio_in_stream_destroy(struct SoundIoInStream *in_stream) {
|
||||
if (!in_stream)
|
||||
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;
|
||||
|
||||
SoundIo *soundio = in_stream->device->soundio;
|
||||
SoundIoInStreamPrivate *is = (SoundIoInStreamPrivate *)instream;
|
||||
SoundIo *soundio = instream->device->soundio;
|
||||
SoundIoPrivate *si = (SoundIoPrivate *)soundio;
|
||||
|
||||
if (soundio->in_stream_destroy)
|
||||
soundio->in_stream_destroy(soundio, in_stream);
|
||||
if (si->instream_destroy)
|
||||
si->instream_destroy(si, is);
|
||||
|
||||
soundio_device_unref(in_stream->device);
|
||||
destroy(in_stream);
|
||||
soundio_device_unref(instream->device);
|
||||
destroy(is);
|
||||
}
|
||||
|
||||
void soundio_destroy_devices_info(SoundIoDevicesInfo *devices_info) {
|
||||
|
@ -432,3 +460,78 @@ void soundio_destroy_devices_info(SoundIoDevicesInfo *devices_info) {
|
|||
|
||||
destroy(devices_info);
|
||||
}
|
||||
|
||||
bool soundio_have_backend(SoundIoBackend backend) {
|
||||
switch (backend) {
|
||||
case SoundIoBackendPulseAudio:
|
||||
#ifdef SOUNDIO_HAVE_PULSEAUDIO
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
case SoundIoBackendAlsa:
|
||||
#ifdef SOUNDIO_HAVE_ALSA
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
case SoundIoBackendDummy:
|
||||
return true;
|
||||
case SoundIoBackendNone:
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int soundio_backend_count(struct SoundIo *soundio) {
|
||||
return array_length(available_backends);
|
||||
}
|
||||
|
||||
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,
|
||||
const SoundIoChannelLayout *target_layout)
|
||||
{
|
||||
for (int i = 0; i < available_layouts_count; i += 1) {
|
||||
const SoundIoChannelLayout *available_layout = &available_layouts[i];
|
||||
if (soundio_channel_layout_equal(target_layout, available_layout))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
for (int i = 0; i < preferred_layouts_count; i += 1) {
|
||||
const SoundIoChannelLayout *preferred_layout = &preferred_layouts[i];
|
||||
if (layout_contains(available_layouts, available_layouts_count, preferred_layout))
|
||||
return preferred_layout;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static int compare_layouts(const void *a, const void *b) {
|
||||
const SoundIoChannelLayout *layout_a = *((SoundIoChannelLayout **)a);
|
||||
const SoundIoChannelLayout *layout_b = *((SoundIoChannelLayout **)b);
|
||||
if (layout_a->channel_count > layout_b->channel_count)
|
||||
return -1;
|
||||
else if (layout_a->channel_count < layout_b->channel_count)
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
void soundio_sort_channel_layouts(struct SoundIoChannelLayout *layouts, int layouts_count) {
|
||||
if (!layouts)
|
||||
return;
|
||||
|
||||
qsort(layouts, layouts_count, sizeof(SoundIoChannelLayout), compare_layouts);
|
||||
}
|
||||
|
||||
void soundio_device_sort_channel_layouts(struct SoundIoDevice *device) {
|
||||
soundio_sort_channel_layouts(device->layouts, device->layout_count);
|
||||
}
|
||||
|
|
165
src/soundio.h
165
src/soundio.h
|
@ -14,7 +14,7 @@
|
|||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif /* __cplusplus */
|
||||
#endif
|
||||
|
||||
struct SoundIo;
|
||||
struct SoundIoDevicesInfo;
|
||||
|
@ -25,6 +25,8 @@ enum SoundIoError {
|
|||
SoundIoErrorInitAudioBackend,
|
||||
SoundIoErrorSystemResources,
|
||||
SoundIoErrorOpeningDevice,
|
||||
SoundIoErrorInvalid,
|
||||
SoundIoErrorBackendUnavailable,
|
||||
};
|
||||
|
||||
enum SoundIoChannelId {
|
||||
|
@ -64,15 +66,6 @@ enum SoundIoChannelId {
|
|||
SoundIoChannelIdBottomCenter,
|
||||
SoundIoChannelIdBottomLeftCenter,
|
||||
SoundIoChannelIdBottomRightCenter,
|
||||
|
||||
SoundIoChannelIdCount,
|
||||
};
|
||||
|
||||
#define SOUNDIO_MAX_CHANNELS 32
|
||||
struct SoundIoChannelLayout {
|
||||
const char *name;
|
||||
int channel_count;
|
||||
enum SoundIoChannelId channels[SOUNDIO_MAX_CHANNELS];
|
||||
};
|
||||
|
||||
enum SoundIoChannelLayoutId {
|
||||
|
@ -160,7 +153,17 @@ enum SoundIoFormat {
|
|||
#error unknown byte order
|
||||
#endif
|
||||
|
||||
// The size of this struct is OK to use.
|
||||
#define SOUNDIO_MAX_CHANNELS 32
|
||||
struct SoundIoChannelLayout {
|
||||
const char *name;
|
||||
int channel_count;
|
||||
enum SoundIoChannelId channels[SOUNDIO_MAX_CHANNELS];
|
||||
};
|
||||
|
||||
// The size of this struct is not part of the API or ABI.
|
||||
struct SoundIoDevice {
|
||||
// Read-only. Set automatically.
|
||||
struct SoundIo *soundio;
|
||||
|
||||
// `name` uniquely identifies this device. `description` is user-friendly
|
||||
|
@ -168,9 +171,14 @@ struct SoundIoDevice {
|
|||
char *name;
|
||||
char *description;
|
||||
|
||||
// If this information is missing due to a `probe_error`, the number of
|
||||
// channels will be zero.
|
||||
struct SoundIoChannelLayout channel_layout;
|
||||
// Channel layouts are handled similarly to sample format; see those docs.
|
||||
// If this information is missing due to a `probe_error`, `layouts`
|
||||
// will be NULL. It's OK to modify this data, for example calling
|
||||
// soundio_sort_channel_layouts on it.
|
||||
// Devices are guaranteed to have at least 1 channel layout.
|
||||
struct SoundIoChannelLayout *layouts;
|
||||
int layout_count;
|
||||
struct SoundIoChannelLayout current_layout;
|
||||
|
||||
// A device is either a raw device or it is a virtual device that is
|
||||
// provided by a software mixing service such as dmix or PulseAudio (see
|
||||
|
@ -187,6 +195,7 @@ struct SoundIoDevice {
|
|||
// `format_count`. If this information is missing due to a probe error,
|
||||
// `formats` will be `NULL`. If `current_format` is unavailable, it will be
|
||||
// set to `SoundIoFormatInvalid`.
|
||||
// Devices are guaranteed to have at least 1 format available.
|
||||
enum SoundIoFormat *formats;
|
||||
int format_count;
|
||||
enum SoundIoFormat current_format;
|
||||
|
@ -194,6 +203,7 @@ struct SoundIoDevice {
|
|||
// Sample rate is handled very similar to sample format; see those docs.
|
||||
// If sample rate information is missing due to a probe error, the field
|
||||
// will be set to zero.
|
||||
// Devices are guaranteed to have at least 1 sample rate available.
|
||||
int sample_rate_min;
|
||||
int sample_rate_max;
|
||||
int sample_rate_current;
|
||||
|
@ -222,66 +232,42 @@ struct SoundIoDevice {
|
|||
int probe_error;
|
||||
};
|
||||
|
||||
// The size of this struct is not part of the API or ABI.
|
||||
struct SoundIoOutStream {
|
||||
void *backend_data;
|
||||
struct SoundIoDevice *device;
|
||||
enum SoundIoFormat format;
|
||||
int sample_rate;
|
||||
struct SoundIoChannelLayout layout;
|
||||
double latency;
|
||||
int bytes_per_frame;
|
||||
|
||||
void *userdata;
|
||||
void (*underrun_callback)(struct SoundIoOutStream *);
|
||||
void (*write_callback)(struct SoundIoOutStream *, int frame_count);
|
||||
|
||||
// computed automatically when you call soundio_outstream_open
|
||||
int bytes_per_frame;
|
||||
};
|
||||
|
||||
// The size of this struct is not part of the API or ABI.
|
||||
struct SoundIoInStream {
|
||||
void *backend_data;
|
||||
struct SoundIoDevice *device;
|
||||
enum SoundIoFormat format;
|
||||
int sample_rate;
|
||||
struct SoundIoChannelLayout layout;
|
||||
double latency;
|
||||
int bytes_per_frame;
|
||||
|
||||
void *userdata;
|
||||
void (*read_callback)(struct SoundIoInStream *);
|
||||
|
||||
// computed automatically when you call soundio_instream_open
|
||||
int bytes_per_frame;
|
||||
};
|
||||
|
||||
// The size of this struct is not part of the API or ABI.
|
||||
struct SoundIo {
|
||||
enum SoundIoBackend current_backend;
|
||||
|
||||
// safe to read without a mutex from a single thread
|
||||
struct SoundIoDevicesInfo *safe_devices_info;
|
||||
|
||||
void *userdata;
|
||||
void (*on_devices_change)(struct SoundIo *);
|
||||
void (*on_events_signal)(struct SoundIo *);
|
||||
|
||||
|
||||
void *backend_data;
|
||||
void (*destroy)(struct SoundIo *);
|
||||
void (*flush_events)(struct SoundIo *);
|
||||
void (*wait_events)(struct SoundIo *);
|
||||
void (*wakeup)(struct SoundIo *);
|
||||
|
||||
int (*out_stream_init)(struct SoundIo *, struct SoundIoOutStream *);
|
||||
void (*out_stream_destroy)(struct SoundIo *, struct SoundIoOutStream *);
|
||||
int (*out_stream_start)(struct SoundIo *, struct SoundIoOutStream *);
|
||||
int (*out_stream_free_count)(struct SoundIo *, struct SoundIoOutStream *);
|
||||
void (*out_stream_begin_write)(struct SoundIo *, struct SoundIoOutStream *,
|
||||
char **data, int *frame_count);
|
||||
void (*out_stream_write)(struct SoundIo *, struct SoundIoOutStream *,
|
||||
char *data, int frame_count);
|
||||
void (*out_stream_clear_buffer)(struct SoundIo *, struct SoundIoOutStream *);
|
||||
|
||||
|
||||
int (*in_stream_init)(struct SoundIo *, struct SoundIoInStream *);
|
||||
void (*in_stream_destroy)(struct SoundIo *, struct SoundIoInStream *);
|
||||
int (*in_stream_start)(struct SoundIo *, struct SoundIoInStream *);
|
||||
void (*in_stream_peek)(struct SoundIo *, struct SoundIoInStream *,
|
||||
const char **data, int *frame_count);
|
||||
void (*in_stream_drop)(struct SoundIo *, struct SoundIoInStream *);
|
||||
void (*in_stream_clear_buffer)(struct SoundIo *, struct SoundIoInStream *);
|
||||
};
|
||||
|
||||
// Main Context
|
||||
|
@ -291,12 +277,23 @@ struct SoundIo {
|
|||
struct SoundIo * soundio_create(void);
|
||||
void soundio_destroy(struct SoundIo *soundio);
|
||||
|
||||
|
||||
// Provided these backends were compiled in, this tries JACK, then PulseAudio,
|
||||
// then ALSA, then CoreAudio, then ASIO, then DirectSound, then OSS, then Dummy.
|
||||
int soundio_connect(struct SoundIo *soundio);
|
||||
// Instead of calling `soundio_connect` you may call this function to try a
|
||||
// specific backend.
|
||||
int soundio_connect_backend(struct SoundIo *soundio, enum SoundIoBackend backend);
|
||||
void soundio_disconnect(struct SoundIo *soundio);
|
||||
|
||||
const char *soundio_strerror(int error);
|
||||
const char *soundio_backend_name(enum SoundIoBackend backend);
|
||||
|
||||
// return the number of available backends
|
||||
int soundio_backend_count(struct SoundIo *soundio);
|
||||
// get the backend at the specified index (0 <= index < soundio_backend_count)
|
||||
enum SoundIoBackend soundio_get_backend(struct SoundIo *soundio, int index);
|
||||
|
||||
// when you call this, the on_devices_change and on_events_signal callbacks
|
||||
// might be called. This is the only time those functions will be called.
|
||||
void soundio_flush_events(struct SoundIo *soundio);
|
||||
|
@ -312,7 +309,10 @@ void soundio_wakeup(struct SoundIo *soundio);
|
|||
|
||||
// Channel Layouts
|
||||
|
||||
bool soundio_channel_layout_equal(const struct SoundIoChannelLayout *a,
|
||||
// Returns whether the channel count field and each channel id matches in
|
||||
// the supplied channel layouts.
|
||||
bool soundio_channel_layout_equal(
|
||||
const struct SoundIoChannelLayout *a,
|
||||
const struct SoundIoChannelLayout *b);
|
||||
|
||||
const char *soundio_get_channel_name(enum SoundIoChannelId id);
|
||||
|
@ -320,15 +320,25 @@ const char *soundio_get_channel_name(enum SoundIoChannelId id);
|
|||
int soundio_channel_layout_builtin_count(void);
|
||||
const struct SoundIoChannelLayout *soundio_channel_layout_get_builtin(int index);
|
||||
|
||||
// TODO remove this API or have it write to a `char *`?
|
||||
void soundio_debug_print_channel_layout(const struct SoundIoChannelLayout *layout);
|
||||
|
||||
int soundio_channel_layout_find_channel(
|
||||
const struct SoundIoChannelLayout *layout, enum SoundIoChannelId channel);
|
||||
|
||||
// merely populates the name field of layout if it matches a builtin one.
|
||||
// Populates the name field of layout if it matches a builtin one.
|
||||
// returns whether it found a match
|
||||
bool soundio_channel_layout_detect_builtin(struct SoundIoChannelLayout *layout);
|
||||
|
||||
// Iterates over preferred_layouts. Returns the first channel layout in
|
||||
// preferred_layouts which matches one of the channel layouts in
|
||||
// available_layouts. Returns NULL if none matches.
|
||||
const struct SoundIoChannelLayout *soundio_best_matching_channel_layout(
|
||||
const struct SoundIoChannelLayout *preferred_layouts, int preferred_layout_count,
|
||||
const struct SoundIoChannelLayout *available_layouts, int available_layout_count);
|
||||
|
||||
// Sorts by channel count, descending.
|
||||
void soundio_sort_channel_layouts(struct SoundIoChannelLayout *layouts, int layout_count);
|
||||
|
||||
|
||||
// Sample Formats
|
||||
|
@ -375,51 +385,52 @@ bool soundio_device_equal(
|
|||
const struct SoundIoDevice *b);
|
||||
enum SoundIoDevicePurpose soundio_device_purpose(const struct SoundIoDevice *device);
|
||||
|
||||
// Sorts channel layouts by channel count, descending.
|
||||
void soundio_device_sort_channel_layouts(struct SoundIoDevice *device);
|
||||
|
||||
|
||||
// Output Devices
|
||||
|
||||
int soundio_out_stream_create(struct SoundIoDevice *device,
|
||||
enum SoundIoFormat format, int sample_rate,
|
||||
double latency, void *userdata,
|
||||
void (*write_callback)(struct SoundIoOutStream *, int frame_count),
|
||||
void (*underrun_callback)(struct SoundIoOutStream *),
|
||||
struct SoundIoOutStream **out_out_stream);
|
||||
void soundio_out_stream_destroy(struct SoundIoOutStream *out_stream);
|
||||
// Output Streams
|
||||
// allocates memory and sets defaults. Next you should fill out the struct fields
|
||||
// and then call soundio_outstream_open
|
||||
struct SoundIoOutStream *soundio_outstream_create(struct SoundIoDevice *device);
|
||||
|
||||
int soundio_out_stream_start(struct SoundIoOutStream *out_stream);
|
||||
int soundio_outstream_open(struct SoundIoOutStream *outstream);
|
||||
|
||||
void soundio_out_stream_fill_with_silence(struct SoundIoOutStream *out_stream);
|
||||
void soundio_outstream_destroy(struct SoundIoOutStream *outstream);
|
||||
|
||||
int soundio_outstream_start(struct SoundIoOutStream *outstream);
|
||||
|
||||
void soundio_outstream_fill_with_silence(struct SoundIoOutStream *outstream);
|
||||
|
||||
|
||||
// number of frames available to write
|
||||
int soundio_out_stream_free_count(struct SoundIoOutStream *out_stream);
|
||||
void soundio_out_stream_begin_write(struct SoundIoOutStream *out_stream,
|
||||
int soundio_outstream_free_count(struct SoundIoOutStream *outstream);
|
||||
void soundio_outstream_begin_write(struct SoundIoOutStream *outstream,
|
||||
char **data, int *frame_count);
|
||||
void soundio_out_stream_write(struct SoundIoOutStream *out_stream,
|
||||
void soundio_outstream_write(struct SoundIoOutStream *outstream,
|
||||
char *data, int frame_count);
|
||||
|
||||
void soundio_out_stream_clear_buffer(struct SoundIoOutStream *out_stream);
|
||||
void soundio_outstream_clear_buffer(struct SoundIoOutStream *outstream);
|
||||
|
||||
|
||||
|
||||
// Input Devices
|
||||
// Input Streams
|
||||
// allocates memory and sets defaults. Next you should fill out the struct fields
|
||||
// and then call soundio_instream_open
|
||||
struct SoundIoInStream *soundio_instream_create(struct SoundIoDevice *device);
|
||||
void soundio_instream_destroy(struct SoundIoInStream *instream);
|
||||
|
||||
int soundio_in_stream_create(struct SoundIoDevice *device,
|
||||
enum SoundIoFormat format, int sample_rate,
|
||||
double latency, void *userdata,
|
||||
void (*read_callback)(struct SoundIoInStream *),
|
||||
struct SoundIoInStream **out_in_stream);
|
||||
void soundio_in_stream_destroy(struct SoundIoInStream *in_stream);
|
||||
int soundio_instream_open(struct SoundIoInStream *instream);
|
||||
|
||||
int soundio_in_stream_start(struct SoundIoInStream *in_stream);
|
||||
int soundio_instream_start(struct SoundIoInStream *instream);
|
||||
|
||||
void soundio_in_stream_peek(struct SoundIoInStream *in_stream,
|
||||
void soundio_instream_peek(struct SoundIoInStream *instream,
|
||||
const char **data, int *out_frame_count);
|
||||
// this will drop all of the frames from when you called soundio_in_stream_peek
|
||||
void soundio_in_stream_drop(struct SoundIoInStream *in_stream);
|
||||
// this will drop all of the frames from when you called soundio_instream_peek
|
||||
void soundio_instream_drop(struct SoundIoInStream *instream);
|
||||
|
||||
void soundio_in_stream_clear_buffer(struct SoundIoInStream *in_stream);
|
||||
void soundio_instream_clear_buffer(struct SoundIoInStream *instream);
|
||||
|
||||
|
||||
// Ring Buffer
|
||||
|
@ -448,6 +459,6 @@ void soundio_ring_buffer_clear(struct SoundIoRingBuffer *ring_buffer);
|
|||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
|
@ -19,6 +19,51 @@ struct SoundIoDevicesInfo {
|
|||
int default_input_index;
|
||||
};
|
||||
|
||||
struct SoundIoOutStreamPrivate {
|
||||
struct SoundIoOutStream pub;
|
||||
void *backend_data;
|
||||
};
|
||||
|
||||
struct SoundIoInStreamPrivate {
|
||||
struct SoundIoInStream pub;
|
||||
void *backend_data;
|
||||
};
|
||||
|
||||
struct SoundIoPrivate {
|
||||
struct SoundIo pub;
|
||||
|
||||
enum SoundIoBackend current_backend;
|
||||
|
||||
// safe to read without a mutex from a single thread
|
||||
struct SoundIoDevicesInfo *safe_devices_info;
|
||||
|
||||
void *backend_data;
|
||||
void (*destroy)(struct SoundIoPrivate *);
|
||||
void (*flush_events)(struct SoundIoPrivate *);
|
||||
void (*wait_events)(struct SoundIoPrivate *);
|
||||
void (*wakeup)(struct SoundIoPrivate *);
|
||||
|
||||
int (*outstream_init)(struct SoundIoPrivate *, struct SoundIoOutStreamPrivate *);
|
||||
void (*outstream_destroy)(struct SoundIoPrivate *, struct SoundIoOutStreamPrivate *);
|
||||
int (*outstream_start)(struct SoundIoPrivate *, struct SoundIoOutStreamPrivate *);
|
||||
int (*outstream_free_count)(struct SoundIoPrivate *, struct SoundIoOutStreamPrivate *);
|
||||
void (*outstream_begin_write)(struct SoundIoPrivate *, struct SoundIoOutStreamPrivate *,
|
||||
char **data, int *frame_count);
|
||||
void (*outstream_write)(struct SoundIoPrivate *, struct SoundIoOutStreamPrivate *,
|
||||
char *data, int frame_count);
|
||||
void (*outstream_clear_buffer)(struct SoundIoPrivate *, struct SoundIoOutStreamPrivate *);
|
||||
|
||||
|
||||
int (*instream_init)(struct SoundIoPrivate *, struct SoundIoInStreamPrivate *);
|
||||
void (*instream_destroy)(struct SoundIoPrivate *, struct SoundIoInStreamPrivate *);
|
||||
int (*instream_start)(struct SoundIoPrivate *, struct SoundIoInStreamPrivate *);
|
||||
void (*instream_peek)(struct SoundIoPrivate *, struct SoundIoInStreamPrivate *,
|
||||
const char **data, int *frame_count);
|
||||
void (*instream_drop)(struct SoundIoPrivate *, struct SoundIoInStreamPrivate *);
|
||||
void (*instream_clear_buffer)(struct SoundIoPrivate *, struct SoundIoInStreamPrivate *);
|
||||
};
|
||||
|
||||
void soundio_destroy_devices_info(struct SoundIoDevicesInfo *devices_info);
|
||||
|
||||
|
||||
#endif
|
||||
|
|
|
@ -26,7 +26,7 @@ static void test_os_get_time(void) {
|
|||
static void write_callback(struct SoundIoOutStream *device, int frame_count) { }
|
||||
static void underrun_callback(struct SoundIoOutStream *device) { }
|
||||
|
||||
static void test_create_out_stream(void) {
|
||||
static void test_create_outstream(void) {
|
||||
struct SoundIo *soundio = soundio_create();
|
||||
assert(soundio);
|
||||
ok_or_panic(soundio_connect(soundio));
|
||||
|
@ -34,10 +34,17 @@ static void test_create_out_stream(void) {
|
|||
assert(default_out_device_index >= 0);
|
||||
struct SoundIoDevice *device = soundio_get_output_device(soundio, default_out_device_index);
|
||||
assert(device);
|
||||
struct SoundIoOutStream *out_stream;
|
||||
soundio_out_stream_create(device, SoundIoFormatFloat32NE, 48000, 0.1, NULL,
|
||||
write_callback, underrun_callback, &out_stream);
|
||||
soundio_out_stream_destroy(out_stream);
|
||||
struct SoundIoOutStream *outstream = soundio_outstream_create(device);
|
||||
outstream->format = SoundIoFormatFloat32NE;
|
||||
outstream->sample_rate = 48000;
|
||||
outstream->layout = device->layouts[0];
|
||||
outstream->latency = 0.1;
|
||||
outstream->write_callback = write_callback;
|
||||
outstream->underrun_callback = underrun_callback;
|
||||
|
||||
ok_or_panic(soundio_outstream_open(outstream));
|
||||
|
||||
soundio_outstream_destroy(outstream);
|
||||
soundio_device_unref(device);
|
||||
soundio_destroy(soundio);
|
||||
}
|
||||
|
@ -161,7 +168,7 @@ struct Test {
|
|||
|
||||
static struct Test tests[] = {
|
||||
{"os_get_time", test_os_get_time},
|
||||
{"create output stream", test_create_out_stream},
|
||||
{"create output stream", test_create_outstream},
|
||||
{"ring buffer basic", test_ring_buffer_basic},
|
||||
{"ring buffer threaded", test_ring_buffer_threaded},
|
||||
{NULL, NULL},
|
||||
|
|
Loading…
Reference in a new issue