microphone example working with dummy backend

This commit is contained in:
Andrew Kelley 2015-07-23 15:04:41 -07:00
parent 057f4ea07e
commit 69764e1afa
10 changed files with 263 additions and 197 deletions

View file

@ -12,10 +12,10 @@ exposed.
## Features
* Supports:
* Supported backends:
- [PulseAudio](http://www.freedesktop.org/wiki/Software/PulseAudio/)
- [ALSA](http://www.alsa-project.org/)
- Dummy Backend (silence)
- Dummy (silence)
- (planned) [JACK](http://jackaudio.org/)
- (planned) [CoreAudio](https://developer.apple.com/library/mac/documentation/MusicAudio/Conceptual/CoreAudioOverview/Introduction/Introduction.html)
- (planned) [WASAPI](https://msdn.microsoft.com/en-us/library/windows/desktop/dd371455%28v=vs.85%29.aspx)
@ -89,7 +89,7 @@ static void write_callback(struct SoundIoOutStream *outstream, int requested_fra
}
seconds_offset += seconds_per_frame * frame_count;
if ((err = soundio_outstream_write(outstream, frame_count)))
if ((err = soundio_outstream_end_write(outstream, frame_count)))
panic("%s", soundio_strerror(err));
requested_frame_count -= frame_count;
@ -98,15 +98,6 @@ static void write_callback(struct SoundIoOutStream *outstream, int requested_fra
}
}
static void error_callback(struct SoundIoOutStream *device, int err) {
if (err == SoundIoErrorUnderflow) {
static int count = 0;
fprintf(stderr, "underrun %d\n", count++);
} else {
panic("%s", soundio_strerror(err));
}
}
int main(int argc, char **argv) {
struct SoundIo *soundio = soundio_create();
if (!soundio)
@ -128,11 +119,13 @@ int main(int argc, char **argv) {
struct SoundIoOutStream *outstream = soundio_outstream_create(device);
outstream->format = SoundIoFormatFloat32NE;
outstream->write_callback = write_callback;
outstream->error_callback = error_callback;
if ((err = soundio_outstream_open(outstream)))
panic("unable to open device: %s", soundio_strerror(err));
if (outstream->layout_error)
fprintf(stderr, "unable to set channel layout: %s\n", soundio_strerror(outstream->layout_error));
if ((err = soundio_outstream_start(outstream)))
panic("unable to start device: %s", soundio_strerror(err));
@ -238,8 +231,10 @@ view `coverage/index.html` in a browser.
## Roadmap
0. pipe record to playback example working with dummy osx, windows
0. pipe record to playback example working with ALSA linux
0. pipe record to playback example working with dummy linux, osx, windows
0. expose prebuf
0. why does pulseaudio microphone use up all the CPU?
0. implement JACK backend, get examples working
0. implement CoreAudio (OSX) backend, get examples working
0. implement WASAPI (Windows) backend, get examples working
@ -266,6 +261,8 @@ view `coverage/index.html` in a browser.
and smaller mlock requirements
0. Consider testing on FreeBSD
0. make rtprio warning a callback and have existing behavior be the default callback
0. write detailed docs on buffer underflows explaining when they occur, what state
changes are related to them, and how to recover from them.
## Planned Uses for libsoundio

View file

@ -64,6 +64,11 @@ static void read_callback(struct SoundIoInStream *instream, int available_frame_
if (frame_count <= 0)
break;
if (!areas) {
// Due to an overflow there is a hole. Fill the ring buffer with
// silence for the size of the hole.
memset(write_ptr, 0, frame_count * instream->bytes_per_frame);
} else {
for (int frame = 0; frame < frame_count; frame += 1) {
for (int ch = 0; ch < instream->layout.channel_count; ch += 1) {
memcpy(write_ptr, areas[ch].ptr, instream->bytes_per_sample);
@ -71,6 +76,7 @@ static void read_callback(struct SoundIoInStream *instream, int available_frame_
write_ptr += instream->bytes_per_sample;
}
}
}
if ((err = soundio_instream_end_read(instream)))
panic("end read error: %s", soundio_strerror(err));
@ -136,16 +142,6 @@ static void write_callback(struct SoundIoOutStream *outstream, int requested_fra
soundio_ring_buffer_advance_read_ptr(ring_buffer, total_read_count * outstream->bytes_per_frame);
}
static void error_callback(struct SoundIoOutStream *outstream, int err) {
if (err == SoundIoErrorUnderflow) {
static int count = 0;
fprintf(stderr, "underrun %d\n", count++);
soundio_outstream_fill_with_silence(outstream);
} else {
panic("error: %s", soundio_strerror(err));
}
}
static int usage(char *exe) {
fprintf(stderr, "Usage: %s [--dummy] [--alsa] [--pulseaudio]\n", exe);
return 1;
@ -239,7 +235,6 @@ int main(int argc, char **argv) {
outstream->buffer_duration = 0.2;
outstream->period_duration = 0.1;
outstream->write_callback = write_callback;
outstream->error_callback = error_callback;
if ((err = soundio_outstream_open(outstream)))
panic("unable to open output stream: %s", soundio_strerror(err));

View file

@ -70,15 +70,6 @@ static void write_callback(struct SoundIoOutStream *outstream, int requested_fra
}
}
static void error_callback(struct SoundIoOutStream *device, int err) {
if (err == SoundIoErrorUnderflow) {
static int count = 0;
fprintf(stderr, "underrun %d\n", count++);
} else {
panic("%s", soundio_strerror(err));
}
}
int main(int argc, char **argv) {
char *exe = argv[0];
enum SoundIoBackend backend = SoundIoBackendNone;
@ -118,7 +109,6 @@ int main(int argc, char **argv) {
struct SoundIoOutStream *outstream = soundio_outstream_create(device);
outstream->format = SoundIoFormatFloat32NE;
outstream->write_callback = write_callback;
outstream->error_callback = error_callback;
if ((err = soundio_outstream_open(outstream)))
panic("unable to open device: %s", soundio_strerror(err));

View file

@ -917,16 +917,18 @@ static int xrun_recovery(SoundIoOutStreamPrivate *os, int err) {
SoundIoOutStream *outstream = &os->pub;
SoundIoOutStreamAlsa *osa = (SoundIoOutStreamAlsa *)os->backend_data;
if (err == -EPIPE) {
outstream->error_callback(outstream, SoundIoErrorUnderflow);
err = snd_pcm_prepare(osa->handle);
if (err >= 0)
outstream->underflow_callback(outstream);
} else if (err == -ESTRPIPE) {
outstream->error_callback(outstream, SoundIoErrorUnderflow);
while ((err = snd_pcm_resume(osa->handle)) == -EAGAIN) {
// wait until suspend flag is released
poll(nullptr, 0, 1);
}
if (err < 0)
err = snd_pcm_prepare(osa->handle);
if (err >= 0)
outstream->underflow_callback(outstream);
}
return err;
}
@ -1202,10 +1204,6 @@ static int outstream_start_alsa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os)
return 0;
}
static int outstream_free_count_alsa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os) {
soundio_panic("TODO");
}
int outstream_begin_write_alsa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os,
struct SoundIoChannelArea **out_areas, int *frame_count)
{
@ -1315,10 +1313,6 @@ static int instream_end_read_alsa(SoundIoPrivate *si, SoundIoInStreamPrivate *is
soundio_panic("TODO");
}
static void instream_clear_buffer_alsa(SoundIoPrivate *si, SoundIoInStreamPrivate *is) {
soundio_panic("TODO");
}
static int instream_pause_alsa(struct SoundIoPrivate *si, struct SoundIoInStreamPrivate *is, bool pause) {
SoundIoInStreamAlsa *isa = (SoundIoInStreamAlsa *) is->backend_data;
int err;
@ -1409,7 +1403,6 @@ int soundio_alsa_init(SoundIoPrivate *si) {
si->outstream_open = outstream_open_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_end_write = outstream_end_write_alsa;
si->outstream_clear_buffer = outstream_clear_buffer_alsa;
@ -1420,7 +1413,6 @@ int soundio_alsa_init(SoundIoPrivate *si) {
si->instream_start = instream_start_alsa;
si->instream_begin_read = instream_begin_read_alsa;
si->instream_end_read = instream_end_read_alsa;
si->instream_clear_buffer = instream_clear_buffer_alsa;
si->instream_pause = instream_pause_alsa;
return 0;

View file

@ -19,13 +19,20 @@ struct SoundIoOutStreamDummy {
struct SoundIoOsThread *thread;
struct SoundIoOsCond *cond;
atomic_flag abort_flag;
int buffer_size;
int buffer_frame_count;
struct SoundIoRingBuffer ring_buffer;
SoundIoChannelArea areas[SOUNDIO_MAX_CHANNELS];
};
struct SoundIoInStreamDummy {
// TODO
struct SoundIoOsThread *thread;
struct SoundIoOsCond *cond;
atomic_flag abort_flag;
int read_frame_count;
int buffer_frame_count;
struct SoundIoRingBuffer ring_buffer;
int hole_size;
SoundIoChannelArea areas[SOUNDIO_MAX_CHANNELS];
};
struct SoundIoDummy {
@ -55,10 +62,10 @@ static void playback_thread_run(void *arg) {
frames_consumed += read_count;
if (frames_left > 0) {
outstream->error_callback(outstream, SoundIoErrorUnderflow);
// simulate filling with silence
int free_count = soundio_ring_buffer_free_count(&osd->ring_buffer);
soundio_ring_buffer_advance_write_ptr(&osd->ring_buffer, free_count);
outstream->underflow_callback(outstream);
// TODO delete this and simulate prebuf
soundio_ring_buffer_advance_write_ptr(&osd->ring_buffer,
soundio_ring_buffer_free_count(&osd->ring_buffer));
} else if (read_count > 0) {
outstream->write_callback(outstream, read_count);
}
@ -71,14 +78,38 @@ static void playback_thread_run(void *arg) {
}
}
/*
static void recording_thread_run(void *arg) {
SoundIoInStream *instream = (SoundIoInStream *)arg;
SoundIoDevice *device = instream->device;
SoundIo *soundio = device->soundio;
// TODO
static void capture_thread_run(void *arg) {
SoundIoInStreamPrivate *is = (SoundIoInStreamPrivate *)arg;
SoundIoInStream *instream = &is->pub;
SoundIoInStreamDummy *isd = (SoundIoInStreamDummy *)is->backend_data;
long frames_consumed = 0;
double start_time = soundio_os_get_time();
while (isd->abort_flag.test_and_set()) {
double now = soundio_os_get_time();
double total_time = now - start_time;
long total_frames = total_time * instream->sample_rate;
int frames_to_kill = total_frames - frames_consumed;
int free_bytes = soundio_ring_buffer_free_count(&isd->ring_buffer);
int free_frames = free_bytes / instream->bytes_per_frame;
int write_count = min(frames_to_kill, free_frames);
int frames_left = frames_to_kill - write_count;
int byte_count = write_count * instream->bytes_per_frame;
soundio_ring_buffer_advance_write_ptr(&isd->ring_buffer, byte_count);
frames_consumed += write_count;
if (frames_left > 0)
isd->hole_size += frames_left;
if (write_count > 0)
instream->read_callback(instream, write_count);
now = soundio_os_get_time();
double time_passed = now - start_time;
double next_period = start_time +
ceil(time_passed / instream->period_duration) * instream->period_duration;
double relative_time = next_period - now;
soundio_os_cond_timed_wait(isd->cond, nullptr, relative_time);
}
}
*/
static void destroy_dummy(SoundIoPrivate *si) {
SoundIoDummy *sid = (SoundIoDummy *)si->backend_data;
@ -153,9 +184,15 @@ static int outstream_open_dummy(SoundIoPrivate *si, SoundIoOutStreamPrivate *os)
}
os->backend_data = osd;
osd->buffer_size = outstream->bytes_per_frame * outstream->sample_rate * outstream->buffer_duration;
soundio_ring_buffer_init(&osd->ring_buffer, osd->buffer_size);
int err;
int buffer_size = outstream->bytes_per_frame * outstream->sample_rate * outstream->buffer_duration;
if ((err = soundio_ring_buffer_init(&osd->ring_buffer, buffer_size))) {
outstream_destroy_dummy(si, os);
return err;
}
int actual_capacity = soundio_ring_buffer_capacity(&osd->ring_buffer);
osd->buffer_frame_count = actual_capacity / outstream->bytes_per_frame;
outstream->buffer_duration = osd->buffer_frame_count / (double) outstream->sample_rate;
osd->cond = soundio_os_cond_create();
if (!osd->cond) {
@ -188,32 +225,24 @@ static int outstream_pause_dummy(struct SoundIoPrivate *si, struct SoundIoOutStr
}
static int outstream_start_dummy(SoundIoPrivate *si, SoundIoOutStreamPrivate *os) {
SoundIoOutStream *outstream = &os->pub;
SoundIoOutStreamDummy *osd = (SoundIoOutStreamDummy *)os->backend_data;
soundio_outstream_fill_with_silence(outstream);
assert(soundio_ring_buffer_fill_count(&osd->ring_buffer) == osd->buffer_size);
// TODO delete this and simulate prebuf
soundio_ring_buffer_advance_write_ptr(&osd->ring_buffer, soundio_ring_buffer_free_count(&osd->ring_buffer));
return outstream_pause_dummy(si, os, false);
}
static int outstream_free_count_dummy(SoundIoPrivate *si, 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 / outstream->bytes_per_frame;
}
static int outstream_begin_write_dummy(SoundIoPrivate *si,
SoundIoOutStreamPrivate *os, SoundIoChannelArea **out_areas, int *frame_count)
{
*out_areas = nullptr;
SoundIoOutStream *outstream = &os->pub;
SoundIoOutStreamDummy *osd = (SoundIoOutStreamDummy *)os->backend_data;
int byte_count = *frame_count * outstream->bytes_per_frame;
assert(byte_count <= osd->buffer_size);
assert(*frame_count >= 0);
assert(*frame_count <= osd->buffer_frame_count);
int free_byte_count = soundio_ring_buffer_free_count(&osd->ring_buffer);
int free_frame_count = free_byte_count / outstream->bytes_per_frame;
@ -244,43 +273,135 @@ static void outstream_clear_buffer_dummy(SoundIoPrivate *si, SoundIoOutStreamPri
soundio_ring_buffer_clear(&osd->ring_buffer);
}
static void instream_destroy_dummy(SoundIoPrivate *si, SoundIoInStreamPrivate *is) {
SoundIoInStreamDummy *isd = (SoundIoInStreamDummy *)is->backend_data;
if (!isd)
return;
if (isd->thread) {
isd->abort_flag.clear();
soundio_os_cond_signal(isd->cond, nullptr);
soundio_os_thread_destroy(isd->thread);
isd->thread = nullptr;
}
soundio_os_cond_destroy(isd->cond);
isd->cond = nullptr;
soundio_ring_buffer_deinit(&isd->ring_buffer);
destroy(isd);
is->backend_data = nullptr;
}
static int instream_open_dummy(SoundIoPrivate *si, SoundIoInStreamPrivate *is) {
/* TODO
SoundIoInStream *instream = &is->pub;
SoundIoDevice *device = instream->device;
if (instream->buffer_duration == 0.0)
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) {
if (instream->period_duration == 0.0) {
instream->period_duration = clamp(instream->device->period_duration_min,
instream->buffer_duration / 8.0, instream->device->period_duration_max);
}
*/
soundio_panic("TODO");
SoundIoInStreamDummy *isd = create<SoundIoInStreamDummy>();
if (!isd) {
instream_destroy_dummy(si, is);
return SoundIoErrorNoMem;
}
is->backend_data = isd;
int err;
int buffer_size = instream->bytes_per_frame * instream->sample_rate * instream->buffer_duration;
if ((err = soundio_ring_buffer_init(&isd->ring_buffer, buffer_size))) {
instream_destroy_dummy(si, is);
return err;
}
static void instream_destroy_dummy(SoundIoPrivate *si, SoundIoInStreamPrivate *is) {
soundio_panic("TODO");
int actual_capacity = soundio_ring_buffer_capacity(&isd->ring_buffer);
isd->buffer_frame_count = actual_capacity / instream->bytes_per_frame;
instream->buffer_duration = isd->buffer_frame_count / (double) instream->sample_rate;
isd->cond = soundio_os_cond_create();
if (!isd->cond) {
instream_destroy_dummy(si, is);
return SoundIoErrorNoMem;
}
return 0;
}
static int instream_pause_dummy(SoundIoPrivate *si, SoundIoInStreamPrivate *is, bool pause) {
SoundIoInStreamDummy *isd = (SoundIoInStreamDummy *)is->backend_data;
if (pause) {
if (isd->thread) {
isd->abort_flag.clear();
soundio_os_cond_signal(isd->cond, nullptr);
soundio_os_thread_destroy(isd->thread);
isd->thread = nullptr;
}
} else {
if (!isd->thread) {
isd->abort_flag.test_and_set();
int err;
if ((err = soundio_os_thread_create(capture_thread_run, is, true, &isd->thread))) {
return err;
}
}
}
return 0;
}
static int instream_start_dummy(SoundIoPrivate *si, SoundIoInStreamPrivate *is) {
soundio_panic("TODO");
return instream_pause_dummy(si, is, false);
}
static int instream_begin_read_dummy(SoundIoPrivate *si,
SoundIoInStreamPrivate *is, SoundIoChannelArea **out_areas, int *frame_count)
{
soundio_panic("TODO");
SoundIoInStream *instream = &is->pub;
SoundIoInStreamDummy *isd = (SoundIoInStreamDummy *)is->backend_data;
assert(*frame_count >= 0);
assert(*frame_count <= isd->buffer_frame_count);
if (isd->hole_size > 0) {
*out_areas = nullptr;
isd->read_frame_count = min(isd->hole_size, *frame_count);
*frame_count = isd->read_frame_count;
return 0;
}
int fill_byte_count = soundio_ring_buffer_fill_count(&isd->ring_buffer);
int fill_frame_count = fill_byte_count / instream->bytes_per_frame;
isd->read_frame_count = min(*frame_count, fill_frame_count);
*frame_count = isd->read_frame_count;
if (fill_frame_count) {
char *read_ptr = soundio_ring_buffer_read_ptr(&isd->ring_buffer);
for (int ch = 0; ch < instream->layout.channel_count; ch += 1) {
isd->areas[ch].ptr = read_ptr + instream->bytes_per_sample * ch;
isd->areas[ch].step = instream->bytes_per_frame;
}
*out_areas = isd->areas;
}
return 0;
}
static int instream_end_read_dummy(SoundIoPrivate *si, SoundIoInStreamPrivate *is) {
soundio_panic("TODO");
SoundIoInStreamDummy *isd = (SoundIoInStreamDummy *)is->backend_data;
SoundIoInStream *instream = &is->pub;
if (isd->hole_size > 0) {
isd->hole_size -= isd->read_frame_count;
return 0;
}
static void instream_clear_buffer_dummy(SoundIoPrivate *si, SoundIoInStreamPrivate *is) {
soundio_panic("TODO");
}
static int instream_pause_dummy(SoundIoPrivate *si, SoundIoInStreamPrivate *is, bool pause) {
soundio_panic("TODO");
int byte_count = isd->read_frame_count * instream->bytes_per_frame;
soundio_ring_buffer_advance_write_ptr(&isd->ring_buffer, byte_count);
return 0;
}
static int set_all_device_formats(SoundIoDevice *device) {
@ -458,7 +579,6 @@ int soundio_dummy_init(SoundIoPrivate *si) {
si->outstream_open = outstream_open_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_end_write = outstream_end_write_dummy;
si->outstream_clear_buffer = outstream_clear_buffer_dummy;
@ -469,7 +589,6 @@ int soundio_dummy_init(SoundIoPrivate *si) {
si->instream_start = instream_start_dummy;
si->instream_begin_read = instream_begin_read_dummy;
si->instream_end_read = instream_end_read_dummy;
si->instream_clear_buffer = instream_clear_buffer_dummy;
si->instream_pause = instream_pause_dummy;
return 0;

View file

@ -702,10 +702,8 @@ struct SoundIoOsMirroredMemory *soundio_os_create_mirrored_memory(size_t request
void soundio_os_destroy_mirrored_memory(struct SoundIoOsMirroredMemory *mem) {
if (!mem)
return;
if (!mem->address)
return;
#if defined(SOUNDIO_OS_WINDOWS)
SoundIoOsMirroredMemoryPrivate *m = (SoundIoOsMirroredMemoryPrivate *)mem;
#if defined(SOUNDIO_OS_WINDOWS)
BOOL ok;
ok = UnmapViewOfFile(mem->address);
assert(ok);
@ -717,4 +715,5 @@ void soundio_os_destroy_mirrored_memory(struct SoundIoOsMirroredMemory *mem) {
int err = munmap(mem->address, 2 * mem->capacity);
assert(!err);
#endif
destroy(m);
}

View file

@ -621,10 +621,9 @@ static void playback_stream_state_callback(pa_stream *stream, void *userdata) {
static void playback_stream_underflow_callback(pa_stream *stream, void *userdata) {
SoundIoOutStream *outstream = (SoundIoOutStream*)userdata;
outstream->error_callback(outstream, SoundIoErrorUnderflow);
outstream->underflow_callback(outstream);
}
static void playback_stream_write_callback(pa_stream *stream, size_t nbytes, void *userdata) {
SoundIoOutStream *outstream = (SoundIoOutStream*)(userdata);
int frame_count = ((int)nbytes) / outstream->bytes_per_frame;
@ -740,13 +739,6 @@ static int outstream_start_pa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os) {
return 0;
}
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 int outstream_begin_write_pa(SoundIoPrivate *si,
SoundIoOutStreamPrivate *os, SoundIoChannelArea **out_areas, int *frame_count)
{
@ -953,8 +945,6 @@ static int instream_start_pa(SoundIoPrivate *si, SoundIoInStreamPrivate *is) {
static int instream_begin_read_pa(SoundIoPrivate *si,
SoundIoInStreamPrivate *is, SoundIoChannelArea **out_areas, int *frame_count)
{
*out_areas = nullptr;
SoundIoInStream *instream = &is->pub;
SoundIoInStreamPulseAudio *ispa = (SoundIoInStreamPulseAudio *)is->backend_data;
pa_stream *stream = ispa->stream;
@ -963,9 +953,13 @@ static int instream_begin_read_pa(SoundIoPrivate *si,
char *data;
size_t nbytes = *frame_count * instream->bytes_per_frame;
if (pa_stream_peek(stream, (const void **)&data, &nbytes))
if (pa_stream_peek(stream, (const void **)&data, &nbytes)) {
*out_areas = nullptr;
*frame_count = 0;
return SoundIoErrorStreaming;
}
if (data) {
for (int ch = 0; ch < instream->layout.channel_count; ch += 1) {
ispa->areas[ch].ptr = data + instream->bytes_per_sample * ch;
ispa->areas[ch].step = instream->bytes_per_frame;
@ -973,6 +967,10 @@ static int instream_begin_read_pa(SoundIoPrivate *si,
*frame_count = nbytes / instream->bytes_per_frame;
*out_areas = ispa->areas;
} else {
*frame_count = nbytes / instream->bytes_per_frame;
*out_areas = nullptr;
}
return 0;
}
@ -986,32 +984,6 @@ static int instream_end_read_pa(SoundIoPrivate *si, SoundIoInStreamPrivate *is)
return 0;
}
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 *)si->backend_data;
pa_threaded_mainloop_lock(sipa->main_loop);
for (;;) {
const char *data;
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))));
if (nbytes == 0)
break;
if (pa_stream_drop(stream))
soundio_panic("pa_stream_drop error: %s", pa_strerror(pa_context_errno(pa_stream_get_context(stream))));
}
pa_threaded_mainloop_unlock(sipa->main_loop);
}
static int instream_pause_pa(SoundIoPrivate *si, SoundIoInStreamPrivate *is, bool pause) {
SoundIoInStreamPulseAudio *ispa = (SoundIoInStreamPulseAudio *)is->backend_data;
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data;
@ -1093,7 +1065,6 @@ int soundio_pulseaudio_init(SoundIoPrivate *si) {
si->outstream_open = outstream_open_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_end_write = outstream_end_write_pa;
si->outstream_clear_buffer = outstream_clear_buffer_pa;
@ -1104,7 +1075,6 @@ int soundio_pulseaudio_init(SoundIoPrivate *si) {
si->instream_start = instream_start_pa;
si->instream_begin_read = instream_begin_read_pa;
si->instream_end_read = instream_end_read_pa;
si->instream_clear_buffer = instream_clear_buffer_pa;
si->instream_pause = instream_pause_pa;
return 0;

View file

@ -42,7 +42,6 @@ const char *soundio_strerror(int error) {
case SoundIoErrorInvalid: return "invalid value";
case SoundIoErrorBackendUnavailable: return "backend unavailable";
case SoundIoErrorStreaming: return "unrecoverable streaming failure";
case SoundIoErrorUnderflow: return "buffer underflow";
case SoundIoErrorIncompatibleDevice: return "incompatible device";
}
soundio_panic("invalid error enum value: %d", error);
@ -216,7 +215,6 @@ void soundio_disconnect(struct SoundIo *soundio) {
si->outstream_open = nullptr;
si->outstream_destroy = nullptr;
si->outstream_start = nullptr;
si->outstream_free_count = nullptr;
si->outstream_begin_write = nullptr;
si->outstream_end_write = nullptr;
si->outstream_clear_buffer = nullptr;
@ -226,7 +224,6 @@ void soundio_disconnect(struct SoundIo *soundio) {
si->instream_start = nullptr;
si->instream_begin_read = nullptr;
si->instream_end_read = nullptr;
si->instream_clear_buffer = nullptr;
}
void soundio_flush_events(struct SoundIo *soundio) {
@ -320,32 +317,6 @@ void soundio_wakeup(struct SoundIo *soundio) {
si->wakeup(si);
}
int soundio_outstream_fill_with_silence(struct SoundIoOutStream *outstream) {
SoundIoChannelArea *areas;
int err;
int requested_frame_count = soundio_outstream_free_count(outstream);
while (requested_frame_count > 0) {
int frame_count = requested_frame_count;
if ((err = soundio_outstream_begin_write(outstream, &areas, &frame_count)))
return err;
for (int frame = 0; frame < frame_count; frame += 1) {
for (int ch = 0; ch < outstream->layout.channel_count; ch += 1) {
memset(areas[ch].ptr + areas[ch].step * frame, 0, outstream->bytes_per_sample);
}
}
soundio_outstream_end_write(outstream, frame_count);
requested_frame_count -= frame_count;
}
return 0;
}
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);
}
int soundio_outstream_begin_write(struct SoundIoOutStream *outstream,
SoundIoChannelArea **areas, int *frame_count)
{
@ -362,6 +333,11 @@ int soundio_outstream_end_write(struct SoundIoOutStream *outstream, int frame_co
return si->outstream_end_write(si, os, frame_count);
}
static void default_outstream_error_callback(struct SoundIoOutStream *os, int err) {
soundio_panic("libsoundio: %s", soundio_strerror(err));
}
static void default_underflow_callback(struct SoundIoOutStream *outstream) { }
struct SoundIoOutStream *soundio_outstream_create(struct SoundIoDevice *device) {
SoundIoOutStreamPrivate *os = create<SoundIoOutStreamPrivate>();
@ -372,6 +348,9 @@ struct SoundIoOutStream *soundio_outstream_create(struct SoundIoDevice *device)
outstream->device = device;
soundio_device_ref(device);
outstream->error_callback = default_outstream_error_callback;
outstream->underflow_callback = default_underflow_callback;
return outstream;
}
@ -437,6 +416,10 @@ int soundio_outstream_pause(struct SoundIoOutStream *outstream, bool pause) {
return si->outstream_pause(si, os, pause);
}
static void default_instream_error_callback(struct SoundIoInStream *is, int err) {
soundio_panic("libsoundio: %s", soundio_strerror(err));
}
struct SoundIoInStream *soundio_instream_create(struct SoundIoDevice *device) {
SoundIoInStreamPrivate *is = create<SoundIoInStreamPrivate>();
if (!is)
@ -446,6 +429,8 @@ struct SoundIoInStream *soundio_instream_create(struct SoundIoDevice *device) {
instream->device = device;
soundio_device_ref(device);
instream->error_callback = default_instream_error_callback;
return instream;
}

View file

@ -24,7 +24,6 @@ enum SoundIoError {
SoundIoErrorOpeningDevice,
SoundIoErrorInvalid,
SoundIoErrorBackendUnavailable,
SoundIoErrorUnderflow,
SoundIoErrorStreaming,
SoundIoErrorIncompatibleDevice,
};
@ -330,13 +329,21 @@ struct SoundIoOutStream {
// Defaults to NULL. Put whatever you want here.
void *userdata;
// `err` is SoundIoErrorUnderflow or SoundIoErrorStreaming.
// SoundIoErrorUnderflow means that the sound device ran out of buffered
// audio data to play. You must write more data to the buffer to recover.
// In this callback, you call `soundio_outstream_begin_write` and
// `soundio_outstream_end_write`.
void (*write_callback)(struct SoundIoOutStream *, int requested_frame_count);
// This optional callback happens when the sound device runs out of buffered
// audio data to play. After this occurs, the outstream waits until the
// buffer is full to resume playback.
// This callback is called in the same thread context as `write_callback`.
void (*underflow_callback)(struct SoundIoOutStream *);
// Optional callback. `err` is always SoundIoErrorStreaming.
// SoundIoErrorStreaming is an unrecoverable error. The stream is in an
// invalid state and must be destroyed.
// If you do not supply `error_callback`, the default callback will print
// a message to stderr and then call `abort`.
// This callback is called in the same thread context as `write_callback`.
void (*error_callback)(struct SoundIoOutStream *, int err);
void (*write_callback)(struct SoundIoOutStream *, int requested_frame_count);
// Name of the stream. This is used by PulseAudio. Defaults to "SoundIo".
const char *name;
@ -378,7 +385,16 @@ struct SoundIoInStream {
// Defaults to NULL. Put whatever you want here.
void *userdata;
// In this function call `soundio_instream_begin_read` and
// `soundio_instream_end_read`.
void (*read_callback)(struct SoundIoInStream *, int available_frame_count);
// Optional callback. `err` is always SoundIoErrorStreaming.
// SoundIoErrorStreaming is an unrecoverable error. The stream is in an
// invalid state and must be destroyed.
// If you do not supply `error_callback`, the default callback will print
// a message to stderr and then abort().
// This is called from the same thread context as `read_callback`.
void (*error_callback)(struct SoundIoInStream *, int err);
// Name of the stream. This is used by PulseAudio. Defaults to "SoundIo".
const char *name;
@ -530,16 +546,11 @@ struct SoundIoOutStream *soundio_outstream_create(struct SoundIoDevice *device);
int soundio_outstream_open(struct SoundIoOutStream *outstream);
// You may not call this function from `write_callback`.
void soundio_outstream_destroy(struct SoundIoOutStream *outstream);
int soundio_outstream_start(struct SoundIoOutStream *outstream);
int soundio_outstream_fill_with_silence(struct SoundIoOutStream *outstream);
// number of frames available to write
int soundio_outstream_free_count(struct SoundIoOutStream *outstream);
// Call this function when you are ready to begin writing to the device buffer.
// * `outstream` - (in) The output stream you want to write to.
// * `areas` - (out) The memory addresses you can write data to. It is OK to
@ -563,6 +574,7 @@ void soundio_outstream_clear_buffer(struct SoundIoOutStream *outstream);
// If the underyling device supports pausing, this pauses the stream and
// prevents `write_callback` from being called. Otherwise this returns
// `SoundIoErrorIncompatibleDevice`.
// You must call this function only from `write_callback`.
int soundio_outstream_pause(struct SoundIoOutStream *outstream, bool pause);
@ -572,6 +584,7 @@ int soundio_outstream_pause(struct SoundIoOutStream *outstream, bool pause);
// 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);
// You must not call this function from `read_callback`.
void soundio_instream_destroy(struct SoundIoInStream *instream);
int soundio_instream_open(struct SoundIoInStream *instream);
@ -582,25 +595,33 @@ int soundio_instream_start(struct SoundIoInStream *instream);
// buffer.
// * `instream` - (in) The input stream you want to read from.
// * `areas` - (out) The memory addresses you can read data from. It is OK
// to modify the pointers if that helps you iterate.
// to modify the pointers if that helps you iterate. If a buffer overflow
// occurred, there will be a "hole" in the buffer. To indicate this,
// `areas` will be `NULL` and `frame_count` tells how big the hole is in
// frames.
// * `frame_count` - (in/out) - Provide the number of frames you want to read.
// Returned will be the number of frames you can actually read.
// It is your responsibility to call this function no more and no fewer than the
// correct number of times as determined by `available_frame_count` from
// `read_callback`. See microphone.c for an example.
// You must call this function only from `read_callback`.
// After calling this function, read data from `areas` and then call
// `soundio_instream_end_read`.
// After calling this function, read data from `areas` and then use
// `soundio_instream_end_read` to actually remove the data from the buffer
// and move the read index forward. `soundio_instream_end_read` should not be
// called if the buffer is empty (`frame_count` == 0), but it should be called
// if there is a hole.
int soundio_instream_begin_read(struct SoundIoInStream *instream,
struct SoundIoChannelArea **areas, int *frame_count);
// This will drop all of the frames from when you called `soundio_instream_begin_read`.
// This will drop all of the frames from when you called
// `soundio_instream_begin_read`.
// You must call this function only from `read_callback` after a successful
// call to `soundio_instream_begin_read`.
int soundio_instream_end_read(struct SoundIoInStream *instream);
void soundio_instream_clear_buffer(struct SoundIoInStream *instream);
// If the underyling device supports pausing, this pauses the stream and
// prevents `read_callback` from being called. Otherwise this returns
// `SoundIoErrorIncompatibleDevice`.
// You must call this function only from `read_callback`.
int soundio_instream_pause(struct SoundIoInStream *instream, bool pause);

View file

@ -46,7 +46,6 @@ struct SoundIoPrivate {
int (*outstream_open)(struct SoundIoPrivate *, struct SoundIoOutStreamPrivate *);
void (*outstream_destroy)(struct SoundIoPrivate *, struct SoundIoOutStreamPrivate *);
int (*outstream_start)(struct SoundIoPrivate *, struct SoundIoOutStreamPrivate *);
int (*outstream_free_count)(struct SoundIoPrivate *, struct SoundIoOutStreamPrivate *);
int (*outstream_begin_write)(struct SoundIoPrivate *, struct SoundIoOutStreamPrivate *,
SoundIoChannelArea **out_areas, int *frame_count);
int (*outstream_end_write)(struct SoundIoPrivate *, struct SoundIoOutStreamPrivate *, int frame_count);
@ -60,7 +59,6 @@ struct SoundIoPrivate {
int (*instream_begin_read)(struct SoundIoPrivate *, struct SoundIoInStreamPrivate *,
SoundIoChannelArea **out_areas, int *frame_count);
int (*instream_end_read)(struct SoundIoPrivate *, struct SoundIoInStreamPrivate *);
void (*instream_clear_buffer)(struct SoundIoPrivate *, struct SoundIoInStreamPrivate *);
int (*instream_pause)(struct SoundIoPrivate *, struct SoundIoInStreamPrivate *, bool pause);
};