mirror of
https://github.com/Ryujinx/libsoundio.git
synced 2024-12-23 04:05:36 +00:00
PulseAudio works again
This commit is contained in:
parent
be2675e551
commit
1eeeff5a24
12
README.md
12
README.md
|
@ -66,8 +66,9 @@ static void write_callback(struct SoundIoOutStream *outstream, int requested_fra
|
||||||
float seconds_per_frame = 1.0f / float_sample_rate;
|
float seconds_per_frame = 1.0f / float_sample_rate;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
int frame_count = requested_frame_count;
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
|
int frame_count = requested_frame_count;
|
||||||
|
|
||||||
struct SoundIoChannelArea *areas;
|
struct SoundIoChannelArea *areas;
|
||||||
if ((err = soundio_outstream_begin_write(outstream, &areas, &frame_count)))
|
if ((err = soundio_outstream_begin_write(outstream, &areas, &frame_count)))
|
||||||
panic("%s", soundio_strerror(err));
|
panic("%s", soundio_strerror(err));
|
||||||
|
@ -90,6 +91,10 @@ static void write_callback(struct SoundIoOutStream *outstream, int requested_fra
|
||||||
|
|
||||||
if ((err = soundio_outstream_write(outstream, frame_count)))
|
if ((err = soundio_outstream_write(outstream, frame_count)))
|
||||||
panic("%s", soundio_strerror(err));
|
panic("%s", soundio_strerror(err));
|
||||||
|
|
||||||
|
requested_frame_count -= frame_count;
|
||||||
|
if (requested_frame_count <= 0)
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -107,7 +112,6 @@ int main(int argc, char **argv) {
|
||||||
if (!soundio)
|
if (!soundio)
|
||||||
panic("out of memory");
|
panic("out of memory");
|
||||||
|
|
||||||
int err;
|
|
||||||
if ((err = soundio_connect(soundio)))
|
if ((err = soundio_connect(soundio)))
|
||||||
panic("error connecting: %s", soundio_strerror(err));
|
panic("error connecting: %s", soundio_strerror(err));
|
||||||
|
|
||||||
|
@ -236,7 +240,6 @@ view `coverage/index.html` in a browser.
|
||||||
|
|
||||||
0. implement ALSA (Linux) backend, get examples working
|
0. implement ALSA (Linux) backend, get examples working
|
||||||
0. ALSA: poll instead of callback
|
0. ALSA: poll instead of callback
|
||||||
0. fix pulseaudio backend since I broke it
|
|
||||||
0. pipe record to playback example working with dummy linux, osx, windows
|
0. pipe record to playback example working with dummy linux, osx, windows
|
||||||
0. pipe record to playback example working with pulseaudio linux
|
0. pipe record to playback example working with pulseaudio linux
|
||||||
0. implement CoreAudio (OSX) backend, get examples working
|
0. implement CoreAudio (OSX) backend, get examples working
|
||||||
|
@ -259,6 +262,9 @@ view `coverage/index.html` in a browser.
|
||||||
0. add len arguments to APIs that have char *
|
0. add len arguments to APIs that have char *
|
||||||
0. custom allocator support
|
0. custom allocator support
|
||||||
0. ALSA: support devices that don't support mmap access
|
0. ALSA: support devices that don't support mmap access
|
||||||
|
0. Test in an app that needs to synchronize video to test the
|
||||||
|
latency/synchronization API.
|
||||||
|
0. Support PulseAudio proplist properties for main context and streams
|
||||||
|
|
||||||
## Planned Uses for libsoundio
|
## Planned Uses for libsoundio
|
||||||
|
|
||||||
|
|
|
@ -38,8 +38,9 @@ static void write_callback(struct SoundIoOutStream *outstream, int requested_fra
|
||||||
float seconds_per_frame = 1.0f / float_sample_rate;
|
float seconds_per_frame = 1.0f / float_sample_rate;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
int frame_count = requested_frame_count;
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
|
int frame_count = requested_frame_count;
|
||||||
|
|
||||||
struct SoundIoChannelArea *areas;
|
struct SoundIoChannelArea *areas;
|
||||||
if ((err = soundio_outstream_begin_write(outstream, &areas, &frame_count)))
|
if ((err = soundio_outstream_begin_write(outstream, &areas, &frame_count)))
|
||||||
panic("%s", soundio_strerror(err));
|
panic("%s", soundio_strerror(err));
|
||||||
|
@ -62,6 +63,10 @@ static void write_callback(struct SoundIoOutStream *outstream, int requested_fra
|
||||||
|
|
||||||
if ((err = soundio_outstream_write(outstream, frame_count)))
|
if ((err = soundio_outstream_write(outstream, frame_count)))
|
||||||
panic("%s", soundio_strerror(err));
|
panic("%s", soundio_strerror(err));
|
||||||
|
|
||||||
|
requested_frame_count -= frame_count;
|
||||||
|
if (requested_frame_count <= 0)
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
10
src/alsa.cpp
10
src/alsa.cpp
|
@ -929,6 +929,15 @@ static void async_direct_callback(snd_async_handler_t *ahandler) {
|
||||||
|
|
||||||
static int outstream_open_alsa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os) {
|
static int outstream_open_alsa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os) {
|
||||||
SoundIoOutStream *outstream = &os->pub;
|
SoundIoOutStream *outstream = &os->pub;
|
||||||
|
SoundIoDevice *device = outstream->device;
|
||||||
|
|
||||||
|
if (outstream->buffer_duration == 0.0)
|
||||||
|
outstream->buffer_duration = clamp(device->buffer_duration_min, 1.0, device->buffer_duration_max);
|
||||||
|
if (outstream->period_duration == 0.0) {
|
||||||
|
outstream->period_duration = clamp(device->period_duration_min,
|
||||||
|
outstream->buffer_duration / 2.0, device->period_duration_max);
|
||||||
|
}
|
||||||
|
|
||||||
SoundIoOutStreamAlsa *osa = create<SoundIoOutStreamAlsa>();
|
SoundIoOutStreamAlsa *osa = create<SoundIoOutStreamAlsa>();
|
||||||
if (!osa) {
|
if (!osa) {
|
||||||
outstream_destroy_alsa(si, os);
|
outstream_destroy_alsa(si, os);
|
||||||
|
@ -1133,6 +1142,7 @@ static int outstream_pause_alsa(struct SoundIoPrivate *si, struct SoundIoOutStre
|
||||||
}
|
}
|
||||||
|
|
||||||
static int instream_open_alsa(SoundIoPrivate *si, SoundIoInStreamPrivate *is) {
|
static int instream_open_alsa(SoundIoPrivate *si, SoundIoInStreamPrivate *is) {
|
||||||
|
// TODO default buffer_duration and period_duration
|
||||||
soundio_panic("TODO");
|
soundio_panic("TODO");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -137,6 +137,15 @@ static void outstream_destroy_dummy(SoundIoPrivate *si, SoundIoOutStreamPrivate
|
||||||
|
|
||||||
static int outstream_open_dummy(SoundIoPrivate *si, SoundIoOutStreamPrivate *os) {
|
static int outstream_open_dummy(SoundIoPrivate *si, SoundIoOutStreamPrivate *os) {
|
||||||
SoundIoOutStream *outstream = &os->pub;
|
SoundIoOutStream *outstream = &os->pub;
|
||||||
|
SoundIoDevice *device = outstream->device;
|
||||||
|
|
||||||
|
if (outstream->buffer_duration == 0.0)
|
||||||
|
outstream->buffer_duration = clamp(device->buffer_duration_min, 1.0, device->buffer_duration_max);
|
||||||
|
if (outstream->period_duration == 0.0) {
|
||||||
|
outstream->period_duration = clamp(device->period_duration_min,
|
||||||
|
outstream->buffer_duration / 2.0, device->period_duration_max);
|
||||||
|
}
|
||||||
|
|
||||||
SoundIoOutStreamDummy *osd = create<SoundIoOutStreamDummy>();
|
SoundIoOutStreamDummy *osd = create<SoundIoOutStreamDummy>();
|
||||||
if (!osd) {
|
if (!osd) {
|
||||||
outstream_destroy_dummy(si, os);
|
outstream_destroy_dummy(si, os);
|
||||||
|
@ -236,6 +245,15 @@ static void outstream_clear_buffer_dummy(SoundIoPrivate *si, SoundIoOutStreamPri
|
||||||
}
|
}
|
||||||
|
|
||||||
static int instream_open_dummy(SoundIoPrivate *si, SoundIoInStreamPrivate *is) {
|
static int instream_open_dummy(SoundIoPrivate *si, SoundIoInStreamPrivate *is) {
|
||||||
|
/* TODO
|
||||||
|
instream->buffer_duration = clamp(device->buffer_duration_min, 1.0, device->buffer_duration_max);
|
||||||
|
instream->period_duration = -1.0;
|
||||||
|
if (instream->period_duration == -1.0) {
|
||||||
|
instream->period_duration = clamp(instream->device->period_duration_min,
|
||||||
|
instream->buffer_duration / 8.0, instream->device->period_duration_max);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
soundio_panic("TODO");
|
soundio_panic("TODO");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -293,6 +311,16 @@ static int set_all_device_formats(SoundIoDevice *device) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int set_all_device_channel_layouts(SoundIoDevice *device) {
|
||||||
|
device->layout_count = soundio_channel_layout_builtin_count();
|
||||||
|
device->layouts = allocate<SoundIoChannelLayout>(device->layout_count);
|
||||||
|
if (!device->layouts)
|
||||||
|
return SoundIoErrorNoMem;
|
||||||
|
for (int i = 0; i < device->layout_count; i += 1)
|
||||||
|
device->layouts[i] = *soundio_channel_layout_get_builtin(i);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
int soundio_dummy_init(SoundIoPrivate *si) {
|
int soundio_dummy_init(SoundIoPrivate *si) {
|
||||||
SoundIo *soundio = &si->pub;
|
SoundIo *soundio = &si->pub;
|
||||||
assert(!si->backend_data);
|
assert(!si->backend_data);
|
||||||
|
@ -342,17 +370,13 @@ int soundio_dummy_init(SoundIoPrivate *si) {
|
||||||
destroy_dummy(si);
|
destroy_dummy(si);
|
||||||
return SoundIoErrorNoMem;
|
return SoundIoErrorNoMem;
|
||||||
}
|
}
|
||||||
device->layout_count = soundio_channel_layout_builtin_count();
|
|
||||||
device->layouts = allocate<SoundIoChannelLayout>(device->layout_count);
|
|
||||||
if (!device->layouts) {
|
|
||||||
soundio_device_unref(device);
|
|
||||||
destroy_dummy(si);
|
|
||||||
return SoundIoErrorNoMem;
|
|
||||||
}
|
|
||||||
for (int i = 0; i < device->layout_count; i += 1)
|
|
||||||
device->layouts[i] = *soundio_channel_layout_get_builtin(i);
|
|
||||||
|
|
||||||
int err;
|
int err;
|
||||||
|
if ((err = set_all_device_channel_layouts(device))) {
|
||||||
|
soundio_device_unref(device);
|
||||||
|
destroy_dummy(si);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
if ((err = set_all_device_formats(device))) {
|
if ((err = set_all_device_formats(device))) {
|
||||||
soundio_device_unref(device);
|
soundio_device_unref(device);
|
||||||
destroy_dummy(si);
|
destroy_dummy(si);
|
||||||
|
@ -395,17 +419,13 @@ int soundio_dummy_init(SoundIoPrivate *si) {
|
||||||
return SoundIoErrorNoMem;
|
return SoundIoErrorNoMem;
|
||||||
}
|
}
|
||||||
|
|
||||||
device->layout_count = soundio_channel_layout_builtin_count();
|
int err;
|
||||||
device->layouts = allocate<SoundIoChannelLayout>(device->layout_count);
|
if ((err = set_all_device_channel_layouts(device))) {
|
||||||
if (!device->layouts) {
|
|
||||||
soundio_device_unref(device);
|
soundio_device_unref(device);
|
||||||
destroy_dummy(si);
|
destroy_dummy(si);
|
||||||
return SoundIoErrorNoMem;
|
return err;
|
||||||
}
|
}
|
||||||
for (int i = 0; i < device->layout_count; i += 1)
|
|
||||||
device->layouts[i] = *soundio_channel_layout_get_builtin(i);
|
|
||||||
|
|
||||||
int err;
|
|
||||||
if ((err = set_all_device_formats(device))) {
|
if ((err = set_all_device_formats(device))) {
|
||||||
soundio_device_unref(device);
|
soundio_device_unref(device);
|
||||||
destroy_dummy(si);
|
destroy_dummy(si);
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
#include <pulse/pulseaudio.h>
|
#include <pulse/pulseaudio.h>
|
||||||
|
|
||||||
|
@ -135,14 +136,7 @@ static void destroy_pa(SoundIoPrivate *si) {
|
||||||
si->backend_data = nullptr;
|
si->backend_data = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* TODO
|
static SoundIoFormat from_pulseaudio_format(pa_sample_spec sample_spec) {
|
||||||
static double usec_to_sec(pa_usec_t usec) {
|
|
||||||
return (double)usec / (double)PA_USEC_PER_SEC;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
static SoundIoFormat format_from_pulseaudio(pa_sample_spec sample_spec) {
|
|
||||||
switch (sample_spec.format) {
|
switch (sample_spec.format) {
|
||||||
case PA_SAMPLE_U8: return SoundIoFormatU8;
|
case PA_SAMPLE_U8: return SoundIoFormatU8;
|
||||||
case PA_SAMPLE_S16LE: return SoundIoFormatS16LE;
|
case PA_SAMPLE_S16LE: return SoundIoFormatS16LE;
|
||||||
|
@ -165,13 +159,6 @@ static SoundIoFormat format_from_pulseaudio(pa_sample_spec sample_spec) {
|
||||||
return SoundIoFormatInvalid;
|
return SoundIoFormatInvalid;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* TODO
|
|
||||||
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) {
|
static SoundIoChannelId from_pulseaudio_channel_pos(pa_channel_position_t pos) {
|
||||||
switch (pos) {
|
switch (pos) {
|
||||||
case PA_CHANNEL_POSITION_MONO: return SoundIoChannelIdFrontCenter;
|
case PA_CHANNEL_POSITION_MONO: return SoundIoChannelIdFrontCenter;
|
||||||
|
@ -214,7 +201,33 @@ static void set_from_pulseaudio_channel_map(pa_channel_map channel_map, SoundIoC
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
static int set_all_device_channel_layouts(SoundIoDevice *device) {
|
||||||
|
device->layout_count = soundio_channel_layout_builtin_count();
|
||||||
|
device->layouts = allocate<SoundIoChannelLayout>(device->layout_count);
|
||||||
|
if (!device->layouts)
|
||||||
|
return SoundIoErrorNoMem;
|
||||||
|
for (int i = 0; i < device->layout_count; i += 1)
|
||||||
|
device->layouts[i] = *soundio_channel_layout_get_builtin(i);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int set_all_device_formats(SoundIoDevice *device) {
|
||||||
|
device->format_count = 9;
|
||||||
|
device->formats = allocate<SoundIoFormat>(device->format_count);
|
||||||
|
if (!device->formats)
|
||||||
|
return SoundIoErrorNoMem;
|
||||||
|
device->formats[0] = SoundIoFormatU8;
|
||||||
|
device->formats[1] = SoundIoFormatS16LE;
|
||||||
|
device->formats[2] = SoundIoFormatS16BE;
|
||||||
|
device->formats[3] = SoundIoFormatFloat32LE;
|
||||||
|
device->formats[4] = SoundIoFormatFloat32BE;
|
||||||
|
device->formats[5] = SoundIoFormatS32LE;
|
||||||
|
device->formats[6] = SoundIoFormatS32BE;
|
||||||
|
device->formats[7] = SoundIoFormatS24LE;
|
||||||
|
device->formats[8] = SoundIoFormatS24BE;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int perform_operation(SoundIoPrivate *si, pa_operation *op) {
|
static int perform_operation(SoundIoPrivate *si, pa_operation *op) {
|
||||||
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data;
|
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data;
|
||||||
|
@ -274,6 +287,7 @@ static void sink_info_callback(pa_context *pulse_context, const pa_sink_info *in
|
||||||
SoundIoPrivate *si = (SoundIoPrivate *)userdata;
|
SoundIoPrivate *si = (SoundIoPrivate *)userdata;
|
||||||
SoundIo *soundio = &si->pub;
|
SoundIo *soundio = &si->pub;
|
||||||
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data;
|
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data;
|
||||||
|
int err;
|
||||||
if (eol) {
|
if (eol) {
|
||||||
sipa->have_sink_list = true;
|
sipa->have_sink_list = true;
|
||||||
finish_device_query(si);
|
finish_device_query(si);
|
||||||
|
@ -288,15 +302,29 @@ static void sink_info_callback(pa_context *pulse_context, const pa_sink_info *in
|
||||||
device->description = strdup(info->description);
|
device->description = strdup(info->description);
|
||||||
if (!device->name || !device->description)
|
if (!device->name || !device->description)
|
||||||
soundio_panic("out of memory");
|
soundio_panic("out of memory");
|
||||||
// TODO determine the list of supported formats and the min and max sample rate
|
|
||||||
// TODO determine the channel layouts supported
|
device->sample_rate_current = info->sample_spec.rate;
|
||||||
//TODO set_from_pulseaudio_channel_map(info->channel_map, &device->channel_layout);
|
// PulseAudio performs resampling, so any value is valid. Let's pick
|
||||||
device->current_format = format_from_pulseaudio(info->sample_spec);
|
// some reasonable min and max values.
|
||||||
// TODO set min, max, current latency
|
device->sample_rate_min = min(8000, device->sample_rate_current);
|
||||||
//device->default_latency = usec_to_sec(info->configured_latency);
|
device->sample_rate_max = max(5644800, device->sample_rate_current);
|
||||||
// TODO set min, max, current sample rate
|
|
||||||
//device->sample_rate_current = sample_rate_from_pulseaudio(info->sample_spec);
|
device->current_format = from_pulseaudio_format(info->sample_spec);
|
||||||
// TODO set min, max, current period size
|
// PulseAudio performs sample format conversion, so any PulseAudio
|
||||||
|
// value is valid.
|
||||||
|
if ((err = set_all_device_formats(device)))
|
||||||
|
soundio_panic("out of memory");
|
||||||
|
|
||||||
|
set_from_pulseaudio_channel_map(info->channel_map, &device->current_layout);
|
||||||
|
// PulseAudio does channel layout remapping, so any channel layout is valid.
|
||||||
|
if ((err = set_all_device_channel_layouts(device)))
|
||||||
|
soundio_panic("out of memory");
|
||||||
|
|
||||||
|
device->buffer_duration_min = 0.10;
|
||||||
|
device->buffer_duration_max = 4.0;
|
||||||
|
|
||||||
|
// "period" is not a recognized concept in PulseAudio.
|
||||||
|
|
||||||
device->purpose = SoundIoDevicePurposeOutput;
|
device->purpose = SoundIoDevicePurposeOutput;
|
||||||
|
|
||||||
if (sipa->current_devices_info->output_devices.append(device))
|
if (sipa->current_devices_info->output_devices.append(device))
|
||||||
|
@ -309,6 +337,7 @@ static void source_info_callback(pa_context *pulse_context, const pa_source_info
|
||||||
SoundIoPrivate *si = (SoundIoPrivate *)userdata;
|
SoundIoPrivate *si = (SoundIoPrivate *)userdata;
|
||||||
SoundIo *soundio = &si->pub;
|
SoundIo *soundio = &si->pub;
|
||||||
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data;
|
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data;
|
||||||
|
int err;
|
||||||
if (eol) {
|
if (eol) {
|
||||||
sipa->have_source_list = true;
|
sipa->have_source_list = true;
|
||||||
finish_device_query(si);
|
finish_device_query(si);
|
||||||
|
@ -323,15 +352,30 @@ static void source_info_callback(pa_context *pulse_context, const pa_source_info
|
||||||
device->description = strdup(info->description);
|
device->description = strdup(info->description);
|
||||||
if (!device->name || !device->description)
|
if (!device->name || !device->description)
|
||||||
soundio_panic("out of memory");
|
soundio_panic("out of memory");
|
||||||
// TODO determine the list of supported formats and the min and max sample rate
|
|
||||||
// TODO determine the channel layouts supported
|
device->sample_rate_current = info->sample_spec.rate;
|
||||||
// TODO set_from_pulseaudio_channel_map(info->channel_map, &device->channel_layout);
|
// PulseAudio performs resampling, so any value is valid. Let's pick
|
||||||
device->current_format = format_from_pulseaudio(info->sample_spec);
|
// some reasonable min and max values.
|
||||||
// TODO set min, max, current latency
|
device->sample_rate_min = min(8000, device->sample_rate_current);
|
||||||
//device->default_latency = usec_to_sec(info->configured_latency);
|
device->sample_rate_max = max(5644800, device->sample_rate_current);
|
||||||
// TODO set min, max, current sample rate
|
|
||||||
//device->sample_rate_current = sample_rate_from_pulseaudio(info->sample_spec);
|
device->current_format = from_pulseaudio_format(info->sample_spec);
|
||||||
// TODO set min, max, current period size
|
// PulseAudio performs sample format conversion, so any PulseAudio
|
||||||
|
// value is valid.
|
||||||
|
if ((err = set_all_device_formats(device)))
|
||||||
|
soundio_panic("out of memory");
|
||||||
|
|
||||||
|
set_from_pulseaudio_channel_map(info->channel_map, &device->current_layout);
|
||||||
|
// PulseAudio does channel layout remapping, so any channel layout is valid.
|
||||||
|
if ((err = set_all_device_channel_layouts(device)))
|
||||||
|
soundio_panic("out of memory");
|
||||||
|
|
||||||
|
device->buffer_duration_min = 0.10;
|
||||||
|
device->buffer_duration_max = 4.0;
|
||||||
|
device->buffer_duration_current = -1.0;
|
||||||
|
|
||||||
|
// "period" is not a recognized concept in PulseAudio.
|
||||||
|
|
||||||
device->purpose = SoundIoDevicePurposeInput;
|
device->purpose = SoundIoDevicePurposeInput;
|
||||||
|
|
||||||
if (sipa->current_devices_info->input_devices.append(device))
|
if (sipa->current_devices_info->input_devices.append(device))
|
||||||
|
@ -625,8 +669,6 @@ static int outstream_open_pa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os) {
|
||||||
sample_spec.channels = outstream->layout.channel_count;
|
sample_spec.channels = outstream->layout.channel_count;
|
||||||
pa_channel_map channel_map = to_pulseaudio_channel_map(&outstream->layout);
|
pa_channel_map channel_map = to_pulseaudio_channel_map(&outstream->layout);
|
||||||
|
|
||||||
// TODO handle period_duration
|
|
||||||
|
|
||||||
ospa->stream = pa_stream_new(sipa->pulse_context, outstream->name, &sample_spec, &channel_map);
|
ospa->stream = pa_stream_new(sipa->pulse_context, outstream->name, &sample_spec, &channel_map);
|
||||||
if (!ospa->stream) {
|
if (!ospa->stream) {
|
||||||
pa_threaded_mainloop_unlock(sipa->main_loop);
|
pa_threaded_mainloop_unlock(sipa->main_loop);
|
||||||
|
@ -637,15 +679,20 @@ static int outstream_open_pa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os) {
|
||||||
pa_stream_set_write_callback(ospa->stream, playback_stream_write_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);
|
pa_stream_set_underflow_callback(ospa->stream, playback_stream_underflow_callback, outstream);
|
||||||
|
|
||||||
|
ospa->buffer_attr.maxlength = UINT32_MAX;
|
||||||
|
ospa->buffer_attr.tlength = UINT32_MAX;
|
||||||
|
ospa->buffer_attr.prebuf = UINT32_MAX;
|
||||||
|
ospa->buffer_attr.minreq = UINT32_MAX;
|
||||||
|
ospa->buffer_attr.fragsize = UINT32_MAX;
|
||||||
|
|
||||||
|
if (outstream->buffer_duration > 0.0) {
|
||||||
int bytes_per_second = outstream->bytes_per_frame * outstream->sample_rate;
|
int bytes_per_second = outstream->bytes_per_frame * outstream->sample_rate;
|
||||||
int buffer_length = outstream->bytes_per_frame *
|
int buffer_length = outstream->bytes_per_frame *
|
||||||
ceil(outstream->buffer_duration * bytes_per_second / (double)outstream->bytes_per_frame);
|
ceil(outstream->buffer_duration * bytes_per_second / (double)outstream->bytes_per_frame);
|
||||||
|
|
||||||
ospa->buffer_attr.maxlength = buffer_length;
|
ospa->buffer_attr.maxlength = buffer_length;
|
||||||
ospa->buffer_attr.tlength = buffer_length;
|
ospa->buffer_attr.tlength = buffer_length;
|
||||||
ospa->buffer_attr.prebuf = 0;
|
}
|
||||||
ospa->buffer_attr.minreq = UINT32_MAX;
|
|
||||||
ospa->buffer_attr.fragsize = UINT32_MAX;
|
|
||||||
|
|
||||||
pa_threaded_mainloop_unlock(sipa->main_loop);
|
pa_threaded_mainloop_unlock(sipa->main_loop);
|
||||||
|
|
||||||
|
@ -659,10 +706,11 @@ static int outstream_start_pa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os) {
|
||||||
|
|
||||||
pa_threaded_mainloop_lock(sipa->main_loop);
|
pa_threaded_mainloop_lock(sipa->main_loop);
|
||||||
|
|
||||||
|
pa_stream_flags_t flags = (outstream->buffer_duration > 0.0) ? PA_STREAM_ADJUST_LATENCY : PA_STREAM_NOFLAGS;
|
||||||
|
|
||||||
int err = pa_stream_connect_playback(ospa->stream,
|
int err = pa_stream_connect_playback(ospa->stream,
|
||||||
outstream->device->name, &ospa->buffer_attr,
|
outstream->device->name, &ospa->buffer_attr,
|
||||||
PA_STREAM_ADJUST_LATENCY, nullptr, nullptr);
|
flags, nullptr, nullptr);
|
||||||
if (err) {
|
if (err) {
|
||||||
pa_threaded_mainloop_unlock(sipa->main_loop);
|
pa_threaded_mainloop_unlock(sipa->main_loop);
|
||||||
return SoundIoErrorOpeningDevice;
|
return SoundIoErrorOpeningDevice;
|
||||||
|
@ -671,8 +719,6 @@ static int outstream_start_pa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os) {
|
||||||
while (!ospa->stream_ready)
|
while (!ospa->stream_ready)
|
||||||
pa_threaded_mainloop_wait(sipa->main_loop);
|
pa_threaded_mainloop_wait(sipa->main_loop);
|
||||||
|
|
||||||
soundio_outstream_fill_with_silence(outstream);
|
|
||||||
|
|
||||||
pa_threaded_mainloop_unlock(sipa->main_loop);
|
pa_threaded_mainloop_unlock(sipa->main_loop);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -784,6 +830,7 @@ static void instream_destroy_pa(SoundIoPrivate *si, SoundIoInStreamPrivate *inst
|
||||||
|
|
||||||
static int instream_open_pa(SoundIoPrivate *si, SoundIoInStreamPrivate *is) {
|
static int instream_open_pa(SoundIoPrivate *si, SoundIoInStreamPrivate *is) {
|
||||||
SoundIoInStream *instream = &is->pub;
|
SoundIoInStream *instream = &is->pub;
|
||||||
|
|
||||||
SoundIoInStreamPulseAudio *ispa = create<SoundIoInStreamPulseAudio>();
|
SoundIoInStreamPulseAudio *ispa = create<SoundIoInStreamPulseAudio>();
|
||||||
if (!ispa) {
|
if (!ispa) {
|
||||||
instream_destroy_pa(si, is);
|
instream_destroy_pa(si, is);
|
||||||
|
@ -803,8 +850,6 @@ static int instream_open_pa(SoundIoPrivate *si, SoundIoInStreamPrivate *is) {
|
||||||
|
|
||||||
pa_channel_map channel_map = to_pulseaudio_channel_map(&instream->layout);
|
pa_channel_map channel_map = to_pulseaudio_channel_map(&instream->layout);
|
||||||
|
|
||||||
// TODO handle period_duration
|
|
||||||
|
|
||||||
ispa->stream = pa_stream_new(sipa->pulse_context, instream->name, &sample_spec, &channel_map);
|
ispa->stream = pa_stream_new(sipa->pulse_context, instream->name, &sample_spec, &channel_map);
|
||||||
if (!ispa->stream) {
|
if (!ispa->stream) {
|
||||||
pa_threaded_mainloop_unlock(sipa->main_loop);
|
pa_threaded_mainloop_unlock(sipa->main_loop);
|
||||||
|
@ -817,15 +862,22 @@ static int instream_open_pa(SoundIoPrivate *si, SoundIoInStreamPrivate *is) {
|
||||||
pa_stream_set_state_callback(stream, recording_stream_state_callback, is);
|
pa_stream_set_state_callback(stream, recording_stream_state_callback, is);
|
||||||
pa_stream_set_read_callback(stream, recording_stream_read_callback, is);
|
pa_stream_set_read_callback(stream, recording_stream_read_callback, is);
|
||||||
|
|
||||||
int bytes_per_second = instream->bytes_per_frame * instream->sample_rate;
|
|
||||||
int buffer_length = instream->bytes_per_frame *
|
|
||||||
ceil(instream->buffer_duration * bytes_per_second / (double)instream->bytes_per_frame);
|
|
||||||
|
|
||||||
ispa->buffer_attr.maxlength = UINT32_MAX;
|
ispa->buffer_attr.maxlength = UINT32_MAX;
|
||||||
ispa->buffer_attr.tlength = UINT32_MAX;
|
ispa->buffer_attr.tlength = UINT32_MAX;
|
||||||
ispa->buffer_attr.prebuf = 0;
|
ispa->buffer_attr.prebuf = UINT32_MAX;
|
||||||
|
ispa->buffer_attr.minreq = UINT32_MAX;
|
||||||
|
ispa->buffer_attr.fragsize = UINT32_MAX;
|
||||||
|
|
||||||
|
if (instream->period_duration > 0.0) {
|
||||||
|
int bytes_per_second = instream->bytes_per_frame * instream->sample_rate;
|
||||||
|
int buffer_length = instream->bytes_per_frame *
|
||||||
|
ceil(instream->period_duration * bytes_per_second / (double)instream->bytes_per_frame);
|
||||||
|
ispa->buffer_attr.maxlength = UINT32_MAX;
|
||||||
|
ispa->buffer_attr.tlength = UINT32_MAX;
|
||||||
|
ispa->buffer_attr.prebuf = UINT32_MAX;
|
||||||
ispa->buffer_attr.minreq = UINT32_MAX;
|
ispa->buffer_attr.minreq = UINT32_MAX;
|
||||||
ispa->buffer_attr.fragsize = buffer_length;
|
ispa->buffer_attr.fragsize = buffer_length;
|
||||||
|
}
|
||||||
|
|
||||||
pa_threaded_mainloop_unlock(sipa->main_loop);
|
pa_threaded_mainloop_unlock(sipa->main_loop);
|
||||||
|
|
||||||
|
@ -838,9 +890,11 @@ static int instream_start_pa(SoundIoPrivate *si, SoundIoInStreamPrivate *is) {
|
||||||
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data;
|
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data;
|
||||||
pa_threaded_mainloop_lock(sipa->main_loop);
|
pa_threaded_mainloop_lock(sipa->main_loop);
|
||||||
|
|
||||||
|
pa_stream_flags_t flags = (instream->period_duration > 0.0) ? PA_STREAM_ADJUST_LATENCY : PA_STREAM_NOFLAGS;
|
||||||
|
|
||||||
int err = pa_stream_connect_record(ispa->stream,
|
int err = pa_stream_connect_record(ispa->stream,
|
||||||
instream->device->name,
|
instream->device->name,
|
||||||
&ispa->buffer_attr, PA_STREAM_ADJUST_LATENCY);
|
&ispa->buffer_attr, flags);
|
||||||
if (err) {
|
if (err) {
|
||||||
pa_threaded_mainloop_unlock(sipa->main_loop);
|
pa_threaded_mainloop_unlock(sipa->main_loop);
|
||||||
return SoundIoErrorOpeningDevice;
|
return SoundIoErrorOpeningDevice;
|
||||||
|
@ -905,6 +959,8 @@ static int instream_pause_pa(SoundIoPrivate *si, SoundIoInStreamPrivate *is, boo
|
||||||
}
|
}
|
||||||
|
|
||||||
int soundio_pulseaudio_init(SoundIoPrivate *si) {
|
int soundio_pulseaudio_init(SoundIoPrivate *si) {
|
||||||
|
SoundIo *soundio = &si->pub;
|
||||||
|
|
||||||
assert(!si->backend_data);
|
assert(!si->backend_data);
|
||||||
SoundIoPulseAudio *sipa = create<SoundIoPulseAudio>();
|
SoundIoPulseAudio *sipa = create<SoundIoPulseAudio>();
|
||||||
if (!sipa) {
|
if (!sipa) {
|
||||||
|
@ -932,12 +988,7 @@ int soundio_pulseaudio_init(SoundIoPrivate *si) {
|
||||||
return SoundIoErrorNoMem;
|
return SoundIoErrorNoMem;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO let the API specify this
|
sipa->pulse_context = pa_context_new_with_proplist(main_loop_api, soundio->app_name, sipa->props);
|
||||||
pa_proplist_sets(sipa->props, PA_PROP_APPLICATION_NAME, "libsoundio");
|
|
||||||
pa_proplist_sets(sipa->props, PA_PROP_APPLICATION_VERSION, SOUNDIO_VERSION_STRING);
|
|
||||||
pa_proplist_sets(sipa->props, PA_PROP_APPLICATION_ID, "me.andrewkelley.libsoundio");
|
|
||||||
|
|
||||||
sipa->pulse_context = pa_context_new_with_proplist(main_loop_api, "SoundIo", sipa->props);
|
|
||||||
if (!sipa->pulse_context) {
|
if (!sipa->pulse_context) {
|
||||||
destroy_pa(si);
|
destroy_pa(si);
|
||||||
return SoundIoErrorNoMem;
|
return SoundIoErrorNoMem;
|
||||||
|
|
|
@ -134,6 +134,7 @@ struct SoundIo * soundio_create(void) {
|
||||||
SoundIo *soundio = &si->pub;
|
SoundIo *soundio = &si->pub;
|
||||||
soundio->on_devices_change = default_on_devices_change;
|
soundio->on_devices_change = default_on_devices_change;
|
||||||
soundio->on_events_signal = default_on_events_signal;
|
soundio->on_events_signal = default_on_events_signal;
|
||||||
|
soundio->app_name = "SoundIo";
|
||||||
return soundio;
|
return soundio;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -387,8 +388,6 @@ struct SoundIoOutStream *soundio_outstream_create(struct SoundIoDevice *device)
|
||||||
SoundIoFormatFloat32NE : device->formats[0];
|
SoundIoFormatFloat32NE : device->formats[0];
|
||||||
outstream->layout = soundio_device_supports_layout(device, stereo) ? *stereo : device->layouts[0];
|
outstream->layout = soundio_device_supports_layout(device, stereo) ? *stereo : device->layouts[0];
|
||||||
outstream->sample_rate = clamp(device->sample_rate_min, 48000, device->sample_rate_max);
|
outstream->sample_rate = clamp(device->sample_rate_min, 48000, device->sample_rate_max);
|
||||||
outstream->buffer_duration = clamp(device->buffer_duration_min, 1.0, device->buffer_duration_max);
|
|
||||||
outstream->period_duration = -1.0;
|
|
||||||
outstream->name = "SoundIo";
|
outstream->name = "SoundIo";
|
||||||
|
|
||||||
return outstream;
|
return outstream;
|
||||||
|
@ -398,11 +397,6 @@ int soundio_outstream_open(struct SoundIoOutStream *outstream) {
|
||||||
if (outstream->format <= SoundIoFormatInvalid)
|
if (outstream->format <= SoundIoFormatInvalid)
|
||||||
return SoundIoErrorInvalid;
|
return SoundIoErrorInvalid;
|
||||||
|
|
||||||
if (outstream->period_duration == -1.0) {
|
|
||||||
outstream->period_duration = clamp(outstream->device->period_duration_min,
|
|
||||||
outstream->buffer_duration / 2.0, outstream->device->period_duration_max);
|
|
||||||
}
|
|
||||||
|
|
||||||
SoundIoOutStreamPrivate *os = (SoundIoOutStreamPrivate *)outstream;
|
SoundIoOutStreamPrivate *os = (SoundIoOutStreamPrivate *)outstream;
|
||||||
outstream->bytes_per_frame = soundio_get_bytes_per_frame(outstream->format, outstream->layout.channel_count);
|
outstream->bytes_per_frame = soundio_get_bytes_per_frame(outstream->format, outstream->layout.channel_count);
|
||||||
outstream->bytes_per_sample = soundio_get_bytes_per_sample(outstream->format);
|
outstream->bytes_per_sample = soundio_get_bytes_per_sample(outstream->format);
|
||||||
|
@ -456,8 +450,6 @@ struct SoundIoInStream *soundio_instream_create(struct SoundIoDevice *device) {
|
||||||
SoundIoFormatFloat32NE : device->formats[0];
|
SoundIoFormatFloat32NE : device->formats[0];
|
||||||
instream->layout = soundio_device_supports_layout(device, stereo) ? *stereo : device->layouts[0];
|
instream->layout = soundio_device_supports_layout(device, stereo) ? *stereo : device->layouts[0];
|
||||||
instream->sample_rate = clamp(device->sample_rate_min, 48000, device->sample_rate_max);
|
instream->sample_rate = clamp(device->sample_rate_min, 48000, device->sample_rate_max);
|
||||||
instream->buffer_duration = clamp(device->buffer_duration_min, 1.0, device->buffer_duration_max);
|
|
||||||
instream->period_duration = -1.0;
|
|
||||||
instream->name = "SoundIo";
|
instream->name = "SoundIo";
|
||||||
|
|
||||||
return instream;
|
return instream;
|
||||||
|
@ -467,11 +459,6 @@ int soundio_instream_open(struct SoundIoInStream *instream) {
|
||||||
if (instream->format <= SoundIoFormatInvalid)
|
if (instream->format <= SoundIoFormatInvalid)
|
||||||
return SoundIoErrorInvalid;
|
return SoundIoErrorInvalid;
|
||||||
|
|
||||||
if (instream->period_duration == -1.0) {
|
|
||||||
instream->period_duration = clamp(instream->device->period_duration_min,
|
|
||||||
instream->buffer_duration / 8.0, instream->device->period_duration_max);
|
|
||||||
}
|
|
||||||
|
|
||||||
instream->bytes_per_frame = soundio_get_bytes_per_frame(instream->format, instream->layout.channel_count);
|
instream->bytes_per_frame = soundio_get_bytes_per_frame(instream->format, instream->layout.channel_count);
|
||||||
instream->bytes_per_sample = soundio_get_bytes_per_sample(instream->format);
|
instream->bytes_per_sample = soundio_get_bytes_per_sample(instream->format);
|
||||||
SoundIo *soundio = instream->device->soundio;
|
SoundIo *soundio = instream->device->soundio;
|
||||||
|
|
|
@ -205,6 +205,9 @@ struct SoundIo {
|
||||||
// to call any soundio functions. You may use this to signal a condition
|
// to call any soundio functions. You may use this to signal a condition
|
||||||
// variable to wake up. Called when soundio_wait_events would be woken up.
|
// variable to wake up. Called when soundio_wait_events would be woken up.
|
||||||
void (*on_events_signal)(struct SoundIo *);
|
void (*on_events_signal)(struct SoundIo *);
|
||||||
|
|
||||||
|
// Optional: Application name. Used by PulseAudio. Defaults to "SoundIo".
|
||||||
|
const char *app_name;
|
||||||
};
|
};
|
||||||
|
|
||||||
// The size of this struct is not part of the API or ABI.
|
// The size of this struct is not part of the API or ABI.
|
||||||
|
@ -254,13 +257,15 @@ struct SoundIoDevice {
|
||||||
int sample_rate_max;
|
int sample_rate_max;
|
||||||
int sample_rate_current;
|
int sample_rate_current;
|
||||||
|
|
||||||
// Buffer duration in seconds.
|
// Buffer duration in seconds. If any values are unknown, they are set to
|
||||||
|
// 0.0. These values are unknown for PulseAudio.
|
||||||
double buffer_duration_min;
|
double buffer_duration_min;
|
||||||
double buffer_duration_max;
|
double buffer_duration_max;
|
||||||
double buffer_duration_current;
|
double buffer_duration_current;
|
||||||
|
|
||||||
// Period duration in seconds. After this much time passes, write_callback
|
// Period duration in seconds. After this much time passes, write_callback
|
||||||
// is called.
|
// is called. If values are unknown, they are set to 0.0. These values are
|
||||||
|
// unknown for PulseAudio.
|
||||||
double period_duration_min;
|
double period_duration_min;
|
||||||
double period_duration_max;
|
double period_duration_max;
|
||||||
double period_duration_current;
|
double period_duration_current;
|
||||||
|
@ -305,6 +310,10 @@ struct SoundIoOutStream {
|
||||||
// After you call soundio_outstream_open this value is replaced with the
|
// After you call soundio_outstream_open this value is replaced with the
|
||||||
// actual duration, as near to this value as possible.
|
// actual duration, as near to this value as possible.
|
||||||
// Defaults to 1 second (and then clamped into range).
|
// Defaults to 1 second (and then clamped into range).
|
||||||
|
// If the device has unknown buffer duration min and max values, you may
|
||||||
|
// still set this. If you set this and the backend is PulseAudio, it
|
||||||
|
// sets `PA_STREAM_ADJUST_LATENCY` and is the value used for `maxlength`
|
||||||
|
// and `tlength`.
|
||||||
double buffer_duration;
|
double buffer_duration;
|
||||||
|
|
||||||
// `period_duration` is the latency; how much time it takes
|
// `period_duration` is the latency; how much time it takes
|
||||||
|
@ -312,6 +321,9 @@ struct SoundIoOutStream {
|
||||||
// After you call `soundio_outstream_open` this value is replaced with the
|
// After you call `soundio_outstream_open` this value is replaced with the
|
||||||
// actual period duration, as near to this value as possible.
|
// actual period duration, as near to this value as possible.
|
||||||
// Defaults to `buffer_duration / 2` (and then clamped into range).
|
// Defaults to `buffer_duration / 2` (and then clamped into range).
|
||||||
|
// If the device has unknown period duration min and max values, you may
|
||||||
|
// still set this. If you set this and the backend is PulseAudio, it
|
||||||
|
// sets `PA_STREAM_ADJUST_LATENCY` and is the value used for `fragsize`.
|
||||||
double period_duration;
|
double period_duration;
|
||||||
|
|
||||||
// Defaults to NULL. Put whatever you want here.
|
// Defaults to NULL. Put whatever you want here.
|
||||||
|
@ -516,8 +528,10 @@ int soundio_outstream_free_count(struct SoundIoOutStream *outstream);
|
||||||
// * `areas` - (out) The memory addresses you can write data to. It is OK to
|
// * `areas` - (out) The memory addresses you can write data to. It is OK to
|
||||||
// modify the pointers if that helps you iterate.
|
// modify the pointers if that helps you iterate.
|
||||||
// * `frame_count` - (in/out) Provide the number of frames you want to write.
|
// * `frame_count` - (in/out) Provide the number of frames you want to write.
|
||||||
// Returned will be the number of frames you actually can write. If this
|
// Returned will be the number of frames you actually can write.
|
||||||
// number is greater than zero, you must call this function again.
|
// It is your responsibility to call this function no more and no fewer than the
|
||||||
|
// correct number of times as determined by `requested_frame_count` from
|
||||||
|
// `write_callback`. See sine.c for an example.
|
||||||
int soundio_outstream_begin_write(struct SoundIoOutStream *outstream,
|
int soundio_outstream_begin_write(struct SoundIoOutStream *outstream,
|
||||||
struct SoundIoChannelArea **areas, int *frame_count);
|
struct SoundIoChannelArea **areas, int *frame_count);
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue