mirror of
https://github.com/Ryujinx/libsoundio.git
synced 2024-12-22 23:05:29 +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;
|
||||
int err;
|
||||
|
||||
int frame_count = requested_frame_count;
|
||||
for (;;) {
|
||||
int frame_count = requested_frame_count;
|
||||
|
||||
struct SoundIoChannelArea *areas;
|
||||
if ((err = soundio_outstream_begin_write(outstream, &areas, &frame_count)))
|
||||
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)))
|
||||
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)
|
||||
panic("out of memory");
|
||||
|
||||
int err;
|
||||
if ((err = soundio_connect(soundio)))
|
||||
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. 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 pulseaudio linux
|
||||
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. custom allocator support
|
||||
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
|
||||
|
||||
|
|
|
@ -38,8 +38,9 @@ static void write_callback(struct SoundIoOutStream *outstream, int requested_fra
|
|||
float seconds_per_frame = 1.0f / float_sample_rate;
|
||||
int err;
|
||||
|
||||
int frame_count = requested_frame_count;
|
||||
for (;;) {
|
||||
int frame_count = requested_frame_count;
|
||||
|
||||
struct SoundIoChannelArea *areas;
|
||||
if ((err = soundio_outstream_begin_write(outstream, &areas, &frame_count)))
|
||||
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)))
|
||||
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) {
|
||||
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>();
|
||||
if (!osa) {
|
||||
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) {
|
||||
// TODO default buffer_duration and period_duration
|
||||
soundio_panic("TODO");
|
||||
}
|
||||
|
||||
|
|
|
@ -137,6 +137,15 @@ static void outstream_destroy_dummy(SoundIoPrivate *si, SoundIoOutStreamPrivate
|
|||
|
||||
static int outstream_open_dummy(SoundIoPrivate *si, SoundIoOutStreamPrivate *os) {
|
||||
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>();
|
||||
if (!osd) {
|
||||
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) {
|
||||
/* 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");
|
||||
}
|
||||
|
||||
|
@ -293,6 +311,16 @@ static int set_all_device_formats(SoundIoDevice *device) {
|
|||
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) {
|
||||
SoundIo *soundio = &si->pub;
|
||||
assert(!si->backend_data);
|
||||
|
@ -342,17 +370,13 @@ int soundio_dummy_init(SoundIoPrivate *si) {
|
|||
destroy_dummy(si);
|
||||
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;
|
||||
if ((err = set_all_device_channel_layouts(device))) {
|
||||
soundio_device_unref(device);
|
||||
destroy_dummy(si);
|
||||
return err;
|
||||
}
|
||||
if ((err = set_all_device_formats(device))) {
|
||||
soundio_device_unref(device);
|
||||
destroy_dummy(si);
|
||||
|
@ -395,17 +419,13 @@ int soundio_dummy_init(SoundIoPrivate *si) {
|
|||
return SoundIoErrorNoMem;
|
||||
}
|
||||
|
||||
device->layout_count = soundio_channel_layout_builtin_count();
|
||||
device->layouts = allocate<SoundIoChannelLayout>(device->layout_count);
|
||||
if (!device->layouts) {
|
||||
int err;
|
||||
if ((err = set_all_device_channel_layouts(device))) {
|
||||
soundio_device_unref(device);
|
||||
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))) {
|
||||
soundio_device_unref(device);
|
||||
destroy_dummy(si);
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <pulse/pulseaudio.h>
|
||||
|
||||
|
@ -135,14 +136,7 @@ static void destroy_pa(SoundIoPrivate *si) {
|
|||
si->backend_data = nullptr;
|
||||
}
|
||||
|
||||
/* TODO
|
||||
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) {
|
||||
static SoundIoFormat from_pulseaudio_format(pa_sample_spec sample_spec) {
|
||||
switch (sample_spec.format) {
|
||||
case PA_SAMPLE_U8: return SoundIoFormatU8;
|
||||
case PA_SAMPLE_S16LE: return SoundIoFormatS16LE;
|
||||
|
@ -165,13 +159,6 @@ static SoundIoFormat format_from_pulseaudio(pa_sample_spec sample_spec) {
|
|||
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) {
|
||||
switch (pos) {
|
||||
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) {
|
||||
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;
|
||||
SoundIo *soundio = &si->pub;
|
||||
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data;
|
||||
int err;
|
||||
if (eol) {
|
||||
sipa->have_sink_list = true;
|
||||
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);
|
||||
if (!device->name || !device->description)
|
||||
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
|
||||
//TODO set_from_pulseaudio_channel_map(info->channel_map, &device->channel_layout);
|
||||
device->current_format = format_from_pulseaudio(info->sample_spec);
|
||||
// TODO set min, max, current latency
|
||||
//device->default_latency = usec_to_sec(info->configured_latency);
|
||||
// TODO set min, max, current sample rate
|
||||
//device->sample_rate_current = sample_rate_from_pulseaudio(info->sample_spec);
|
||||
// TODO set min, max, current period size
|
||||
|
||||
device->sample_rate_current = info->sample_spec.rate;
|
||||
// PulseAudio performs resampling, so any value is valid. Let's pick
|
||||
// some reasonable min and max values.
|
||||
device->sample_rate_min = min(8000, device->sample_rate_current);
|
||||
device->sample_rate_max = max(5644800, device->sample_rate_current);
|
||||
|
||||
device->current_format = from_pulseaudio_format(info->sample_spec);
|
||||
// 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;
|
||||
|
||||
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;
|
||||
SoundIo *soundio = &si->pub;
|
||||
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data;
|
||||
int err;
|
||||
if (eol) {
|
||||
sipa->have_source_list = true;
|
||||
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);
|
||||
if (!device->name || !device->description)
|
||||
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
|
||||
// TODO set_from_pulseaudio_channel_map(info->channel_map, &device->channel_layout);
|
||||
device->current_format = format_from_pulseaudio(info->sample_spec);
|
||||
// TODO set min, max, current latency
|
||||
//device->default_latency = usec_to_sec(info->configured_latency);
|
||||
// TODO set min, max, current sample rate
|
||||
//device->sample_rate_current = sample_rate_from_pulseaudio(info->sample_spec);
|
||||
// TODO set min, max, current period size
|
||||
|
||||
device->sample_rate_current = info->sample_spec.rate;
|
||||
// PulseAudio performs resampling, so any value is valid. Let's pick
|
||||
// some reasonable min and max values.
|
||||
device->sample_rate_min = min(8000, device->sample_rate_current);
|
||||
device->sample_rate_max = max(5644800, device->sample_rate_current);
|
||||
|
||||
device->current_format = from_pulseaudio_format(info->sample_spec);
|
||||
// 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;
|
||||
|
||||
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;
|
||||
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);
|
||||
if (!ospa->stream) {
|
||||
pa_threaded_mainloop_unlock(sipa->main_loop);
|
||||
|
@ -637,16 +679,21 @@ static int outstream_open_pa(SoundIoPrivate *si, SoundIoOutStreamPrivate *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 = outstream->bytes_per_frame * outstream->sample_rate;
|
||||
int buffer_length = 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.tlength = buffer_length;
|
||||
ospa->buffer_attr.prebuf = 0;
|
||||
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 buffer_length = 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.tlength = buffer_length;
|
||||
}
|
||||
|
||||
pa_threaded_mainloop_unlock(sipa->main_loop);
|
||||
|
||||
return 0;
|
||||
|
@ -659,10 +706,11 @@ static int outstream_start_pa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os) {
|
|||
|
||||
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,
|
||||
outstream->device->name, &ospa->buffer_attr,
|
||||
PA_STREAM_ADJUST_LATENCY, nullptr, nullptr);
|
||||
flags, nullptr, nullptr);
|
||||
if (err) {
|
||||
pa_threaded_mainloop_unlock(sipa->main_loop);
|
||||
return SoundIoErrorOpeningDevice;
|
||||
|
@ -671,8 +719,6 @@ static int outstream_start_pa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os) {
|
|||
while (!ospa->stream_ready)
|
||||
pa_threaded_mainloop_wait(sipa->main_loop);
|
||||
|
||||
soundio_outstream_fill_with_silence(outstream);
|
||||
|
||||
pa_threaded_mainloop_unlock(sipa->main_loop);
|
||||
|
||||
return 0;
|
||||
|
@ -784,6 +830,7 @@ static void instream_destroy_pa(SoundIoPrivate *si, SoundIoInStreamPrivate *inst
|
|||
|
||||
static int instream_open_pa(SoundIoPrivate *si, SoundIoInStreamPrivate *is) {
|
||||
SoundIoInStream *instream = &is->pub;
|
||||
|
||||
SoundIoInStreamPulseAudio *ispa = create<SoundIoInStreamPulseAudio>();
|
||||
if (!ispa) {
|
||||
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);
|
||||
|
||||
// TODO handle period_duration
|
||||
|
||||
ispa->stream = pa_stream_new(sipa->pulse_context, instream->name, &sample_spec, &channel_map);
|
||||
if (!ispa->stream) {
|
||||
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_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.tlength = UINT32_MAX;
|
||||
ispa->buffer_attr.prebuf = 0;
|
||||
ispa->buffer_attr.prebuf = UINT32_MAX;
|
||||
ispa->buffer_attr.minreq = UINT32_MAX;
|
||||
ispa->buffer_attr.fragsize = buffer_length;
|
||||
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.fragsize = buffer_length;
|
||||
}
|
||||
|
||||
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;
|
||||
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,
|
||||
instream->device->name,
|
||||
&ispa->buffer_attr, PA_STREAM_ADJUST_LATENCY);
|
||||
&ispa->buffer_attr, flags);
|
||||
if (err) {
|
||||
pa_threaded_mainloop_unlock(sipa->main_loop);
|
||||
return SoundIoErrorOpeningDevice;
|
||||
|
@ -905,6 +959,8 @@ static int instream_pause_pa(SoundIoPrivate *si, SoundIoInStreamPrivate *is, boo
|
|||
}
|
||||
|
||||
int soundio_pulseaudio_init(SoundIoPrivate *si) {
|
||||
SoundIo *soundio = &si->pub;
|
||||
|
||||
assert(!si->backend_data);
|
||||
SoundIoPulseAudio *sipa = create<SoundIoPulseAudio>();
|
||||
if (!sipa) {
|
||||
|
@ -932,12 +988,7 @@ int soundio_pulseaudio_init(SoundIoPrivate *si) {
|
|||
return SoundIoErrorNoMem;
|
||||
}
|
||||
|
||||
// TODO let the API specify this
|
||||
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);
|
||||
sipa->pulse_context = pa_context_new_with_proplist(main_loop_api, soundio->app_name, sipa->props);
|
||||
if (!sipa->pulse_context) {
|
||||
destroy_pa(si);
|
||||
return SoundIoErrorNoMem;
|
||||
|
|
|
@ -134,6 +134,7 @@ struct SoundIo * soundio_create(void) {
|
|||
SoundIo *soundio = &si->pub;
|
||||
soundio->on_devices_change = default_on_devices_change;
|
||||
soundio->on_events_signal = default_on_events_signal;
|
||||
soundio->app_name = "SoundIo";
|
||||
return soundio;
|
||||
}
|
||||
|
||||
|
@ -387,8 +388,6 @@ struct SoundIoOutStream *soundio_outstream_create(struct SoundIoDevice *device)
|
|||
SoundIoFormatFloat32NE : device->formats[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->buffer_duration = clamp(device->buffer_duration_min, 1.0, device->buffer_duration_max);
|
||||
outstream->period_duration = -1.0;
|
||||
outstream->name = "SoundIo";
|
||||
|
||||
return outstream;
|
||||
|
@ -398,11 +397,6 @@ int soundio_outstream_open(struct SoundIoOutStream *outstream) {
|
|||
if (outstream->format <= SoundIoFormatInvalid)
|
||||
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;
|
||||
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);
|
||||
|
@ -456,8 +450,6 @@ struct SoundIoInStream *soundio_instream_create(struct SoundIoDevice *device) {
|
|||
SoundIoFormatFloat32NE : device->formats[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->buffer_duration = clamp(device->buffer_duration_min, 1.0, device->buffer_duration_max);
|
||||
instream->period_duration = -1.0;
|
||||
instream->name = "SoundIo";
|
||||
|
||||
return instream;
|
||||
|
@ -467,11 +459,6 @@ int soundio_instream_open(struct SoundIoInStream *instream) {
|
|||
if (instream->format <= SoundIoFormatInvalid)
|
||||
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_sample = soundio_get_bytes_per_sample(instream->format);
|
||||
SoundIo *soundio = instream->device->soundio;
|
||||
|
|
|
@ -205,6 +205,9 @@ struct SoundIo {
|
|||
// 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.
|
||||
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.
|
||||
|
@ -254,13 +257,15 @@ struct SoundIoDevice {
|
|||
int sample_rate_max;
|
||||
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_max;
|
||||
double buffer_duration_current;
|
||||
|
||||
// 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_max;
|
||||
double period_duration_current;
|
||||
|
@ -305,6 +310,10 @@ struct SoundIoOutStream {
|
|||
// After you call soundio_outstream_open this value is replaced with the
|
||||
// actual duration, as near to this value as possible.
|
||||
// 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;
|
||||
|
||||
// `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
|
||||
// actual period duration, as near to this value as possible.
|
||||
// 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;
|
||||
|
||||
// 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
|
||||
// modify the pointers if that helps you iterate.
|
||||
// * `frame_count` - (in/out) Provide the number of frames you want to write.
|
||||
// Returned will be the number of frames you actually can write. If this
|
||||
// number is greater than zero, you must call this function again.
|
||||
// Returned will be the number of frames you actually can write.
|
||||
// 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,
|
||||
struct SoundIoChannelArea **areas, int *frame_count);
|
||||
|
||||
|
|
Loading…
Reference in a new issue