microphone example working with PulseAudio

This commit is contained in:
Andrew Kelley 2015-07-22 15:43:45 -07:00
parent a3388e792a
commit e7dc300cc6
9 changed files with 269 additions and 88 deletions

View file

@ -238,7 +238,6 @@ view `coverage/index.html` in a browser.
## Roadmap ## Roadmap
0. pipe record to playback example working with pulseaudio linux
0. pipe record to playback example working with ALSA linux 0. pipe record to playback example working with ALSA linux
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. implement JACK backend, get examples working 0. implement JACK backend, get examples working

View file

@ -47,12 +47,93 @@ static void panic(const char *format, ...) {
abort(); abort();
} }
static void read_callback(struct SoundIoInStream *instream) { struct SoundIoRingBuffer *ring_buffer = NULL;
fprintf(stderr, "read_callback\n");
static void read_callback(struct SoundIoInStream *instream, int available_frame_count) {
int err;
struct SoundIoChannelArea *areas;
char *write_ptr = soundio_ring_buffer_write_ptr(ring_buffer);
int frames_left = available_frame_count;
for (;;) {
int frame_count = frames_left;
if ((err = soundio_instream_begin_read(instream, &areas, &frame_count)))
panic("begin read error: %s", soundio_strerror(err));
if (frame_count <= 0)
break;
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);
areas[ch].ptr += areas[ch].step;
write_ptr += instream->bytes_per_sample;
}
}
if ((err = soundio_instream_end_read(instream)))
panic("end read error: %s", soundio_strerror(err));
frames_left -= frame_count;
if (frames_left <= 0)
break;
}
int advance_frames = available_frame_count * instream->bytes_per_frame;
soundio_ring_buffer_advance_write_ptr(ring_buffer, advance_frames);
} }
static void write_callback(struct SoundIoOutStream *outstream, int requested_frame_count) { static void write_callback(struct SoundIoOutStream *outstream, int requested_frame_count) {
fprintf(stderr, "write_callback\n"); int err;
struct SoundIoChannelArea *areas;
char *read_ptr = soundio_ring_buffer_read_ptr(ring_buffer);
int fill_count = soundio_ring_buffer_fill_count(ring_buffer) / outstream->bytes_per_frame;
int frames_left = requested_frame_count;
int silence_count = frames_left - fill_count;
if (silence_count < 0)
silence_count = 0;
int total_read_count = requested_frame_count - silence_count;
for (;;) {
int frame_count = frames_left;
if ((err = soundio_outstream_begin_write(outstream, &areas, &frame_count)))
panic("begin write error: %s", soundio_strerror(err));
if (frame_count <= 0)
break;
int silence_frame_count = (silence_count < frame_count) ? silence_count : frame_count;
int read_count = frame_count - silence_frame_count;
for (int frame = 0; frame < silence_frame_count; frame += 1) {
for (int ch = 0; ch < outstream->layout.channel_count; ch += 1) {
memset(areas[ch].ptr, 0, outstream->bytes_per_sample);
areas[ch].ptr += areas[ch].step;
}
}
for (int frame = 0; frame < read_count; frame += 1) {
for (int ch = 0; ch < outstream->layout.channel_count; ch += 1) {
memcpy(areas[ch].ptr, read_ptr, outstream->bytes_per_sample);
areas[ch].ptr += areas[ch].step;
read_ptr += outstream->bytes_per_sample;
}
}
if ((err = soundio_outstream_end_write(outstream, frame_count)))
panic("end write error: %s", soundio_strerror(err));
frames_left -= frame_count;
if (frames_left <= 0)
break;
}
soundio_ring_buffer_advance_read_ptr(ring_buffer, total_read_count * outstream->bytes_per_frame);
} }
static void error_callback(struct SoundIoOutStream *outstream, int err) { static void error_callback(struct SoundIoOutStream *outstream, int err) {
@ -65,14 +146,32 @@ static void error_callback(struct SoundIoOutStream *outstream, int err) {
} }
} }
static int usage(char *exe) {
fprintf(stderr, "Usage: %s [--dummy] [--alsa] [--pulseaudio]\n", exe);
return 1;
}
int main(int argc, char **argv) { int main(int argc, char **argv) {
char *exe = argv[0];
enum SoundIoBackend backend = SoundIoBackendNone;
for (int i = 1; i < argc; i += 1) {
char *arg = argv[i];
if (strcmp("--dummy", arg) == 0) {
backend = SoundIoBackendDummy;
} else if (strcmp("--alsa", arg) == 0) {
backend = SoundIoBackendAlsa;
} else if (strcmp("--pulseaudio", arg) == 0) {
backend = SoundIoBackendPulseAudio;
} else {
return usage(exe);
}
}
struct SoundIo *soundio = soundio_create(); struct SoundIo *soundio = soundio_create();
if (!soundio) if (!soundio)
panic("out of memory"); panic("out of memory");
int err; int err = (backend == SoundIoBackendNone) ?
if ((err = soundio_connect(soundio))) soundio_connect(soundio) : soundio_connect_backend(soundio, backend);
panic("error connecting: %s", soundio_strerror(err));
int default_out_device_index = soundio_get_default_output_device_index(soundio); int default_out_device_index = soundio_get_default_output_device_index(soundio);
if (default_out_device_index < 0) if (default_out_device_index < 0)
@ -125,7 +224,7 @@ int main(int argc, char **argv) {
instream->format = *fmt; instream->format = *fmt;
instream->sample_rate = sample_rate; instream->sample_rate = sample_rate;
instream->layout = *layout; instream->layout = *layout;
instream->buffer_duration = 0.1; instream->period_duration = 0.1;
instream->read_callback = read_callback; instream->read_callback = read_callback;
if ((err = soundio_instream_open(instream))) if ((err = soundio_instream_open(instream)))
@ -137,13 +236,19 @@ int main(int argc, char **argv) {
outstream->format = *fmt; outstream->format = *fmt;
outstream->sample_rate = sample_rate; outstream->sample_rate = sample_rate;
outstream->layout = *layout; outstream->layout = *layout;
outstream->buffer_duration = 0.1; outstream->buffer_duration = 0.2;
outstream->period_duration = 0.1;
outstream->write_callback = write_callback; outstream->write_callback = write_callback;
outstream->error_callback = error_callback; outstream->error_callback = error_callback;
if ((err = soundio_outstream_open(outstream))) if ((err = soundio_outstream_open(outstream)))
panic("unable to open output stream: %s", soundio_strerror(err)); panic("unable to open output stream: %s", soundio_strerror(err));
int capacity = 0.4 * instream->sample_rate * instream->bytes_per_frame;
ring_buffer = soundio_ring_buffer_create(soundio, capacity);
if (!ring_buffer)
panic("unable to create ring buffer: out of memory");
if ((err = soundio_instream_start(instream))) if ((err = soundio_instream_start(instream)))
panic("unable to start input device: %s", soundio_strerror(err)); panic("unable to start input device: %s", soundio_strerror(err));

View file

@ -61,7 +61,7 @@ static void write_callback(struct SoundIoOutStream *outstream, int requested_fra
} }
seconds_offset += seconds_per_frame * frame_count; 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)); panic("%s", soundio_strerror(err));
requested_frame_count -= frame_count; requested_frame_count -= frame_count;

View file

@ -1252,7 +1252,7 @@ int outstream_begin_write_alsa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os,
return 0; return 0;
} }
static int outstream_write_alsa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os, int frame_count) { static int outstream_end_write_alsa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os, int frame_count) {
SoundIoOutStreamAlsa *osa = (SoundIoOutStreamAlsa *) os->backend_data; SoundIoOutStreamAlsa *osa = (SoundIoOutStreamAlsa *) os->backend_data;
SoundIoOutStream *outstream = &os->pub; SoundIoOutStream *outstream = &os->pub;
@ -1305,13 +1305,13 @@ static int instream_start_alsa(SoundIoPrivate *si, SoundIoInStreamPrivate *is) {
soundio_panic("TODO"); soundio_panic("TODO");
} }
static void instream_peek_alsa(SoundIoPrivate *si, static int instream_begin_read_alsa(SoundIoPrivate *si,
SoundIoInStreamPrivate *is, const char **data, int *frame_count) SoundIoInStreamPrivate *is, SoundIoChannelArea **out_areas, int *frame_count)
{ {
soundio_panic("TODO"); soundio_panic("TODO");
} }
static void instream_drop_alsa(SoundIoPrivate *si, SoundIoInStreamPrivate *is) { static int instream_end_read_alsa(SoundIoPrivate *si, SoundIoInStreamPrivate *is) {
soundio_panic("TODO"); soundio_panic("TODO");
} }
@ -1411,15 +1411,15 @@ int soundio_alsa_init(SoundIoPrivate *si) {
si->outstream_start = outstream_start_alsa; si->outstream_start = outstream_start_alsa;
si->outstream_free_count = outstream_free_count_alsa; si->outstream_free_count = outstream_free_count_alsa;
si->outstream_begin_write = outstream_begin_write_alsa; si->outstream_begin_write = outstream_begin_write_alsa;
si->outstream_write = outstream_write_alsa; si->outstream_end_write = outstream_end_write_alsa;
si->outstream_clear_buffer = outstream_clear_buffer_alsa; si->outstream_clear_buffer = outstream_clear_buffer_alsa;
si->outstream_pause = outstream_pause_alsa; si->outstream_pause = outstream_pause_alsa;
si->instream_open = instream_open_alsa; si->instream_open = instream_open_alsa;
si->instream_destroy = instream_destroy_alsa; si->instream_destroy = instream_destroy_alsa;
si->instream_start = instream_start_alsa; si->instream_start = instream_start_alsa;
si->instream_peek = instream_peek_alsa; si->instream_begin_read = instream_begin_read_alsa;
si->instream_drop = instream_drop_alsa; si->instream_end_read = instream_end_read_alsa;
si->instream_clear_buffer = instream_clear_buffer_alsa; si->instream_clear_buffer = instream_clear_buffer_alsa;
si->instream_pause = instream_pause_alsa; si->instream_pause = instream_pause_alsa;

View file

@ -231,7 +231,7 @@ static int outstream_begin_write_dummy(SoundIoPrivate *si,
return 0; return 0;
} }
static int outstream_write_dummy(SoundIoPrivate *si, SoundIoOutStreamPrivate *os, int frame_count) { static int outstream_end_write_dummy(SoundIoPrivate *si, SoundIoOutStreamPrivate *os, int frame_count) {
SoundIoOutStreamDummy *osd = (SoundIoOutStreamDummy *)os->backend_data; SoundIoOutStreamDummy *osd = (SoundIoOutStreamDummy *)os->backend_data;
SoundIoOutStream *outstream = &os->pub; SoundIoOutStream *outstream = &os->pub;
int byte_count = frame_count * outstream->bytes_per_frame; int byte_count = frame_count * outstream->bytes_per_frame;
@ -265,13 +265,13 @@ static int instream_start_dummy(SoundIoPrivate *si, SoundIoInStreamPrivate *is)
soundio_panic("TODO"); soundio_panic("TODO");
} }
static void instream_peek_dummy(SoundIoPrivate *si, static int instream_begin_read_dummy(SoundIoPrivate *si,
SoundIoInStreamPrivate *is, const char **data, int *frame_count) SoundIoInStreamPrivate *is, SoundIoChannelArea **out_areas, int *frame_count)
{ {
soundio_panic("TODO"); soundio_panic("TODO");
} }
static void instream_drop_dummy(SoundIoPrivate *si, SoundIoInStreamPrivate *is) { static int instream_end_read_dummy(SoundIoPrivate *si, SoundIoInStreamPrivate *is) {
soundio_panic("TODO"); soundio_panic("TODO");
} }
@ -460,15 +460,15 @@ int soundio_dummy_init(SoundIoPrivate *si) {
si->outstream_start = outstream_start_dummy; si->outstream_start = outstream_start_dummy;
si->outstream_free_count = outstream_free_count_dummy; si->outstream_free_count = outstream_free_count_dummy;
si->outstream_begin_write = outstream_begin_write_dummy; si->outstream_begin_write = outstream_begin_write_dummy;
si->outstream_write = outstream_write_dummy; si->outstream_end_write = outstream_end_write_dummy;
si->outstream_clear_buffer = outstream_clear_buffer_dummy; si->outstream_clear_buffer = outstream_clear_buffer_dummy;
si->outstream_pause = outstream_pause_dummy; si->outstream_pause = outstream_pause_dummy;
si->instream_open = instream_open_dummy; si->instream_open = instream_open_dummy;
si->instream_destroy = instream_destroy_dummy; si->instream_destroy = instream_destroy_dummy;
si->instream_start = instream_start_dummy; si->instream_start = instream_start_dummy;
si->instream_peek = instream_peek_dummy; si->instream_begin_read = instream_begin_read_dummy;
si->instream_drop = instream_drop_dummy; si->instream_end_read = instream_end_read_dummy;
si->instream_clear_buffer = instream_clear_buffer_dummy; si->instream_clear_buffer = instream_clear_buffer_dummy;
si->instream_pause = instream_pause_dummy; si->instream_pause = instream_pause_dummy;

View file

@ -19,14 +19,15 @@ struct SoundIoOutStreamPulseAudio {
pa_stream *stream; pa_stream *stream;
atomic_bool stream_ready; atomic_bool stream_ready;
pa_buffer_attr buffer_attr; pa_buffer_attr buffer_attr;
SoundIoChannelArea areas[SOUNDIO_MAX_CHANNELS];
char *write_ptr; char *write_ptr;
SoundIoChannelArea areas[SOUNDIO_MAX_CHANNELS];
}; };
struct SoundIoInStreamPulseAudio { struct SoundIoInStreamPulseAudio {
pa_stream *stream; pa_stream *stream;
atomic_bool stream_ready; atomic_bool stream_ready;
pa_buffer_attr buffer_attr; pa_buffer_attr buffer_attr;
SoundIoChannelArea areas[SOUNDIO_MAX_CHANNELS];
}; };
struct SoundIoPulseAudio { struct SoundIoPulseAudio {
@ -730,6 +731,10 @@ 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);
const pa_buffer_attr *attr = pa_stream_get_buffer_attr(ospa->stream);
outstream->buffer_duration = (attr->maxlength /
(double)outstream->bytes_per_frame) / (double)outstream->sample_rate;
pa_threaded_mainloop_unlock(sipa->main_loop); pa_threaded_mainloop_unlock(sipa->main_loop);
return 0; return 0;
@ -765,11 +770,13 @@ static int outstream_begin_write_pa(SoundIoPrivate *si,
return 0; return 0;
} }
static int outstream_write_pa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os, int frame_count) { static int outstream_end_write_pa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os, int frame_count) {
SoundIoOutStream *outstream = &os->pub; SoundIoOutStream *outstream = &os->pub;
SoundIoOutStreamPulseAudio *ospa = (SoundIoOutStreamPulseAudio *)os->backend_data; SoundIoOutStreamPulseAudio *ospa = (SoundIoOutStreamPulseAudio *)os->backend_data;
pa_stream *stream = ospa->stream; pa_stream *stream = ospa->stream;
assert(frame_count > 0);
size_t byte_count = frame_count * outstream->bytes_per_frame; size_t byte_count = frame_count * outstream->bytes_per_frame;
assert(byte_count < 1 * 1024 * 1024 * 1024); // attempting to write > 1GB is certainly a mistake
if (pa_stream_write(stream, ospa->write_ptr, byte_count, NULL, 0, PA_SEEK_RELATIVE)) if (pa_stream_write(stream, ospa->write_ptr, byte_count, NULL, 0, PA_SEEK_RELATIVE))
return SoundIoErrorStreaming; return SoundIoErrorStreaming;
return 0; return 0;
@ -810,6 +817,10 @@ static int outstream_pause_pa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os, b
static void recording_stream_state_callback(pa_stream *stream, void *userdata) { static void recording_stream_state_callback(pa_stream *stream, void *userdata) {
SoundIoInStreamPrivate *is = (SoundIoInStreamPrivate*)userdata; SoundIoInStreamPrivate *is = (SoundIoInStreamPrivate*)userdata;
SoundIoInStreamPulseAudio *ispa = (SoundIoInStreamPulseAudio *)is->backend_data; SoundIoInStreamPulseAudio *ispa = (SoundIoInStreamPulseAudio *)is->backend_data;
SoundIoInStream *instream = &is->pub;
SoundIo *soundio = instream->device->soundio;
SoundIoPrivate *si = (SoundIoPrivate *)soundio;
SoundIoPulseAudio *sipa = (SoundIoPulseAudio *)si->backend_data;
switch (pa_stream_get_state(stream)) { switch (pa_stream_get_state(stream)) {
case PA_STREAM_UNCONNECTED: case PA_STREAM_UNCONNECTED:
case PA_STREAM_CREATING: case PA_STREAM_CREATING:
@ -817,6 +828,7 @@ static void recording_stream_state_callback(pa_stream *stream, void *userdata) {
break; break;
case PA_STREAM_READY: case PA_STREAM_READY:
ispa->stream_ready = true; ispa->stream_ready = true;
pa_threaded_mainloop_signal(sipa->main_loop, 0);
break; break;
case PA_STREAM_FAILED: case PA_STREAM_FAILED:
soundio_panic("pulseaudio stream error: %s", soundio_panic("pulseaudio stream error: %s",
@ -828,7 +840,10 @@ static void recording_stream_state_callback(pa_stream *stream, void *userdata) {
static void recording_stream_read_callback(pa_stream *stream, size_t nbytes, void *userdata) { static void recording_stream_read_callback(pa_stream *stream, size_t nbytes, void *userdata) {
SoundIoInStreamPrivate *is = (SoundIoInStreamPrivate*)userdata; SoundIoInStreamPrivate *is = (SoundIoInStreamPrivate*)userdata;
SoundIoInStream *instream = &is->pub; SoundIoInStream *instream = &is->pub;
instream->read_callback(instream); assert(nbytes % instream->bytes_per_frame == 0);
assert(nbytes > 0);
int available_frame_count = nbytes / instream->bytes_per_frame;
instream->read_callback(instream, available_frame_count);
} }
static void instream_destroy_pa(SoundIoPrivate *si, SoundIoInStreamPrivate *instream) { static void instream_destroy_pa(SoundIoPrivate *si, SoundIoInStreamPrivate *instream) {
@ -924,32 +939,51 @@ static int instream_start_pa(SoundIoPrivate *si, SoundIoInStreamPrivate *is) {
return SoundIoErrorOpeningDevice; return SoundIoErrorOpeningDevice;
} }
while (!ispa->stream_ready)
pa_threaded_mainloop_wait(sipa->main_loop);
const pa_buffer_attr *attr = pa_stream_get_buffer_attr(ispa->stream);
instream->buffer_duration = (attr->maxlength /
(double)instream->bytes_per_frame) / (double)instream->sample_rate;
pa_threaded_mainloop_unlock(sipa->main_loop); pa_threaded_mainloop_unlock(sipa->main_loop);
return 0; return 0;
} }
static void instream_peek_pa(SoundIoPrivate *si, static int instream_begin_read_pa(SoundIoPrivate *si,
SoundIoInStreamPrivate *is, const char **data, int *frame_count) SoundIoInStreamPrivate *is, SoundIoChannelArea **out_areas, int *frame_count)
{ {
*out_areas = nullptr;
SoundIoInStream *instream = &is->pub; SoundIoInStream *instream = &is->pub;
SoundIoInStreamPulseAudio *ispa = (SoundIoInStreamPulseAudio *)is->backend_data; SoundIoInStreamPulseAudio *ispa = (SoundIoInStreamPulseAudio *)is->backend_data;
pa_stream *stream = ispa->stream; pa_stream *stream = ispa->stream;
if (ispa->stream_ready) {
size_t nbytes; assert(ispa->stream_ready);
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)))); char *data;
*frame_count = ((int)nbytes) / instream->bytes_per_frame; size_t nbytes = *frame_count * instream->bytes_per_frame;
} else { if (pa_stream_peek(stream, (const void **)&data, &nbytes))
*data = nullptr; return SoundIoErrorStreaming;
*frame_count = 0;
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;
} }
*frame_count = nbytes / instream->bytes_per_frame;
*out_areas = ispa->areas;
return 0;
} }
static void instream_drop_pa(SoundIoPrivate *si, SoundIoInStreamPrivate *is) { static int instream_end_read_pa(SoundIoPrivate *si, SoundIoInStreamPrivate *is) {
SoundIoInStreamPulseAudio *ispa = (SoundIoInStreamPulseAudio *)is->backend_data; SoundIoInStreamPulseAudio *ispa = (SoundIoInStreamPulseAudio *)is->backend_data;
pa_stream *stream = ispa->stream; pa_stream *stream = ispa->stream;
if (pa_stream_drop(stream)) if (pa_stream_drop(stream))
soundio_panic("pa_stream_drop error: %s", pa_strerror(pa_context_errno(pa_stream_get_context(stream)))); return SoundIoErrorStreaming;
return 0;
} }
static void instream_clear_buffer_pa(SoundIoPrivate *si, SoundIoInStreamPrivate *is) { static void instream_clear_buffer_pa(SoundIoPrivate *si, SoundIoInStreamPrivate *is) {
@ -1061,15 +1095,15 @@ int soundio_pulseaudio_init(SoundIoPrivate *si) {
si->outstream_start = outstream_start_pa; si->outstream_start = outstream_start_pa;
si->outstream_free_count = outstream_free_count_pa; si->outstream_free_count = outstream_free_count_pa;
si->outstream_begin_write = outstream_begin_write_pa; si->outstream_begin_write = outstream_begin_write_pa;
si->outstream_write = outstream_write_pa; si->outstream_end_write = outstream_end_write_pa;
si->outstream_clear_buffer = outstream_clear_buffer_pa; si->outstream_clear_buffer = outstream_clear_buffer_pa;
si->outstream_pause = outstream_pause_pa; si->outstream_pause = outstream_pause_pa;
si->instream_open = instream_open_pa; si->instream_open = instream_open_pa;
si->instream_destroy = instream_destroy_pa; si->instream_destroy = instream_destroy_pa;
si->instream_start = instream_start_pa; si->instream_start = instream_start_pa;
si->instream_peek = instream_peek_pa; si->instream_begin_read = instream_begin_read_pa;
si->instream_drop = instream_drop_pa; si->instream_end_read = instream_end_read_pa;
si->instream_clear_buffer = instream_clear_buffer_pa; si->instream_clear_buffer = instream_clear_buffer_pa;
si->instream_pause = instream_pause_pa; si->instream_pause = instream_pause_pa;

View file

@ -218,14 +218,14 @@ void soundio_disconnect(struct SoundIo *soundio) {
si->outstream_start = nullptr; si->outstream_start = nullptr;
si->outstream_free_count = nullptr; si->outstream_free_count = nullptr;
si->outstream_begin_write = nullptr; si->outstream_begin_write = nullptr;
si->outstream_write = nullptr; si->outstream_end_write = nullptr;
si->outstream_clear_buffer = nullptr; si->outstream_clear_buffer = nullptr;
si->instream_open = nullptr; si->instream_open = nullptr;
si->instream_destroy = nullptr; si->instream_destroy = nullptr;
si->instream_start = nullptr; si->instream_start = nullptr;
si->instream_peek = nullptr; si->instream_begin_read = nullptr;
si->instream_drop = nullptr; si->instream_end_read = nullptr;
si->instream_clear_buffer = nullptr; si->instream_clear_buffer = nullptr;
} }
@ -343,7 +343,7 @@ int soundio_outstream_fill_with_silence(struct SoundIoOutStream *outstream) {
memset(areas[ch].ptr + areas[ch].step * frame, 0, outstream->bytes_per_sample); memset(areas[ch].ptr + areas[ch].step * frame, 0, outstream->bytes_per_sample);
} }
} }
soundio_outstream_write(outstream, frame_count); soundio_outstream_end_write(outstream, frame_count);
requested_frame_count -= frame_count; requested_frame_count -= frame_count;
} }
return 0; return 0;
@ -365,11 +365,11 @@ int soundio_outstream_begin_write(struct SoundIoOutStream *outstream,
return si->outstream_begin_write(si, os, areas, frame_count); return si->outstream_begin_write(si, os, areas, frame_count);
} }
int soundio_outstream_write(struct SoundIoOutStream *outstream, int frame_count) { int soundio_outstream_end_write(struct SoundIoOutStream *outstream, int frame_count) {
SoundIo *soundio = outstream->device->soundio; SoundIo *soundio = outstream->device->soundio;
SoundIoPrivate *si = (SoundIoPrivate *)soundio; SoundIoPrivate *si = (SoundIoPrivate *)soundio;
SoundIoOutStreamPrivate *os = (SoundIoOutStreamPrivate *)outstream; SoundIoOutStreamPrivate *os = (SoundIoOutStreamPrivate *)outstream;
return si->outstream_write(si, os, frame_count); return si->outstream_end_write(si, os, frame_count);
} }
@ -522,6 +522,22 @@ int soundio_instream_pause(struct SoundIoInStream *instream, bool pause) {
return si->instream_pause(si, is, pause); return si->instream_pause(si, is, pause);
} }
int soundio_instream_begin_read(struct SoundIoInStream *instream,
struct SoundIoChannelArea **areas, int *frame_count)
{
SoundIo *soundio = instream->device->soundio;
SoundIoPrivate *si = (SoundIoPrivate *)soundio;
SoundIoInStreamPrivate *is = (SoundIoInStreamPrivate *)instream;
return si->instream_begin_read(si, is, areas, frame_count);
}
int soundio_instream_end_read(struct SoundIoInStream *instream) {
SoundIo *soundio = instream->device->soundio;
SoundIoPrivate *si = (SoundIoPrivate *)soundio;
SoundIoInStreamPrivate *is = (SoundIoInStreamPrivate *)instream;
return si->instream_end_read(si, is);
}
void soundio_destroy_devices_info(SoundIoDevicesInfo *devices_info) { void soundio_destroy_devices_info(SoundIoDevicesInfo *devices_info) {
if (!devices_info) if (!devices_info)
return; return;

View file

@ -111,24 +111,24 @@ enum SoundIoDevicePurpose {
enum SoundIoFormat { enum SoundIoFormat {
SoundIoFormatInvalid, SoundIoFormatInvalid,
SoundIoFormatS8, // Signed 8 bit SoundIoFormatS8, // Signed 8 bit
SoundIoFormatU8, // Unsigned 8 bit SoundIoFormatU8, // Unsigned 8 bit
SoundIoFormatS16LE, // Signed 16 bit Little Endian SoundIoFormatS16LE, // Signed 16 bit Little Endian
SoundIoFormatS16BE, // Signed 16 bit Big Endian SoundIoFormatS16BE, // Signed 16 bit Big Endian
SoundIoFormatU16LE, // Unsigned 16 bit Little Endian SoundIoFormatU16LE, // Unsigned 16 bit Little Endian
SoundIoFormatU16BE, // Unsigned 16 bit Little Endian SoundIoFormatU16BE, // Unsigned 16 bit Little Endian
SoundIoFormatS24LE, // Signed 24 bit Little Endian using low three bytes in 32-bit word SoundIoFormatS24LE, // Signed 24 bit Little Endian using low three bytes in 32-bit word
SoundIoFormatS24BE, // Signed 24 bit Big Endian using low three bytes in 32-bit word SoundIoFormatS24BE, // Signed 24 bit Big Endian using low three bytes in 32-bit word
SoundIoFormatU24LE, // Unsigned 24 bit Little Endian using low three bytes in 32-bit word SoundIoFormatU24LE, // Unsigned 24 bit Little Endian using low three bytes in 32-bit word
SoundIoFormatU24BE, // Unsigned 24 bit Big Endian using low three bytes in 32-bit word SoundIoFormatU24BE, // Unsigned 24 bit Big Endian using low three bytes in 32-bit word
SoundIoFormatS32LE, // Signed 32 bit Little Endian SoundIoFormatS32LE, // Signed 32 bit Little Endian
SoundIoFormatS32BE, // Signed 32 bit Big Endian SoundIoFormatS32BE, // Signed 32 bit Big Endian
SoundIoFormatU32LE, // Unsigned 32 bit Little Endian SoundIoFormatU32LE, // Unsigned 32 bit Little Endian
SoundIoFormatU32BE, // Unsigned 32 bit Big Endian SoundIoFormatU32BE, // Unsigned 32 bit Big Endian
SoundIoFormatFloat32LE, // Float 32 bit Little Endian, Range -1.0 to 1.0 SoundIoFormatFloat32LE, // Float 32 bit Little Endian, Range -1.0 to 1.0
SoundIoFormatFloat32BE, // Float 32 bit Big Endian, Range -1.0 to 1.0 SoundIoFormatFloat32BE, // Float 32 bit Big Endian, Range -1.0 to 1.0
SoundIoFormatFloat64LE, // Float 64 bit Little Endian, Range -1.0 to 1.0 SoundIoFormatFloat64LE, // Float 64 bit Little Endian, Range -1.0 to 1.0
SoundIoFormatFloat64BE, // Float 64 bit Big Endian, Range -1.0 to 1.0 SoundIoFormatFloat64BE, // Float 64 bit Big Endian, Range -1.0 to 1.0
}; };
// For your convenience, Native Endian and Foreign Endian constants are defined // For your convenience, Native Endian and Foreign Endian constants are defined
@ -249,6 +249,7 @@ struct SoundIoDevice {
int format_count; int format_count;
enum SoundIoFormat current_format; enum SoundIoFormat current_format;
// Sample rate is the number of frames per second.
// Sample rate is handled very similar to sample format; see those docs. // Sample rate is handled very similar to sample format; see those docs.
// If sample rate information is missing due to a probe error, the field // If sample rate information is missing due to a probe error, the field
// will be set to zero. // will be set to zero.
@ -300,6 +301,7 @@ struct SoundIoOutStream {
// Defaults to SoundIoFormatFloat32NE, followed by the first one supported. // Defaults to SoundIoFormatFloat32NE, followed by the first one supported.
enum SoundIoFormat format; enum SoundIoFormat format;
// Sample rate is the number of frames per second.
// Defaults to 48000 (and then clamped into range). // Defaults to 48000 (and then clamped into range).
int sample_rate; int sample_rate;
@ -357,6 +359,7 @@ struct SoundIoInStream {
// Defaults to SoundIoFormatFloat32NE, followed by the first one supported. // Defaults to SoundIoFormatFloat32NE, followed by the first one supported.
enum SoundIoFormat format; enum SoundIoFormat format;
// Sample rate is the number of frames per second.
// Defaults to max(sample_rate_min, min(sample_rate_max, 48000)) // Defaults to max(sample_rate_min, min(sample_rate_max, 48000))
int sample_rate; int sample_rate;
@ -375,7 +378,7 @@ struct SoundIoInStream {
// Defaults to NULL. Put whatever you want here. // Defaults to NULL. Put whatever you want here.
void *userdata; void *userdata;
void (*read_callback)(struct SoundIoInStream *); void (*read_callback)(struct SoundIoInStream *, int available_frame_count);
// Name of the stream. This is used by PulseAudio. Defaults to "SoundIo". // Name of the stream. This is used by PulseAudio. Defaults to "SoundIo".
const char *name; const char *name;
@ -404,9 +407,10 @@ void soundio_disconnect(struct SoundIo *soundio);
const char *soundio_strerror(int error); const char *soundio_strerror(int error);
const char *soundio_backend_name(enum SoundIoBackend backend); const char *soundio_backend_name(enum SoundIoBackend backend);
// return the number of available backends // Returns the number of available backends.
int soundio_backend_count(struct SoundIo *soundio); int soundio_backend_count(struct SoundIo *soundio);
// get the available backend at the specified index (0 <= index < soundio_backend_count) // get the available backend at the specified index
// (0 <= index < `soundio_backend_count`)
enum SoundIoBackend soundio_get_backend(struct SoundIo *soundio, int index); enum SoundIoBackend soundio_get_backend(struct SoundIo *soundio, int index);
// when you call this, the on_devices_change and on_events_signal callbacks // when you call this, the on_devices_change and on_events_signal callbacks
@ -461,6 +465,7 @@ static inline int soundio_get_bytes_per_frame(enum SoundIoFormat format, int cha
return soundio_get_bytes_per_sample(format) * channel_count; return soundio_get_bytes_per_sample(format) * channel_count;
} }
// Sample rate is the number of frames per second.
static inline int soundio_get_bytes_per_second(enum SoundIoFormat format, static inline int soundio_get_bytes_per_second(enum SoundIoFormat format,
int channel_count, int sample_rate) int channel_count, int sample_rate)
{ {
@ -477,9 +482,11 @@ const char * soundio_format_string(enum SoundIoFormat format);
int soundio_get_input_device_count(struct SoundIo *soundio); int soundio_get_input_device_count(struct SoundIo *soundio);
int soundio_get_output_device_count(struct SoundIo *soundio); int soundio_get_output_device_count(struct SoundIo *soundio);
// returns NULL on error // Always returns a device. Call soundio_device_unref when done.
// call soundio_device_unref when you no longer have a reference to the pointer. // `index` must be 0 <= index < soundio_get_input_device_count
struct SoundIoDevice *soundio_get_input_device(struct SoundIo *soundio, int index); struct SoundIoDevice *soundio_get_input_device(struct SoundIo *soundio, int index);
// Always returns a device. Call soundio_device_unref when done.
// `index` must be 0 <= index < soundio_get_output_device_count
struct SoundIoDevice *soundio_get_output_device(struct SoundIo *soundio, int index); struct SoundIoDevice *soundio_get_output_device(struct SoundIo *soundio, int index);
// returns the index of the default input device // returns the index of the default input device
@ -514,8 +521,8 @@ bool soundio_device_supports_layout(struct SoundIoDevice *device,
// Output Streams // Output Streams
// allocates memory and sets defaults. Next you should fill out the struct fields // Allocates memory and sets defaults. Next you should fill out the struct fields
// and then call soundio_outstream_open // and then call `soundio_outstream_open`.
struct SoundIoOutStream *soundio_outstream_create(struct SoundIoDevice *device); struct SoundIoOutStream *soundio_outstream_create(struct SoundIoDevice *device);
int soundio_outstream_open(struct SoundIoOutStream *outstream); int soundio_outstream_open(struct SoundIoOutStream *outstream);
@ -539,10 +546,14 @@ int soundio_outstream_free_count(struct SoundIoOutStream *outstream);
// It is your responsibility to call this function no more and no fewer than the // 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 // correct number of times as determined by `requested_frame_count` from
// `write_callback`. See sine.c for an example. // `write_callback`. See sine.c for an example.
// You must call this function only from `write_callback`. After calling this
// function, write data to `areas` and then call `soundio_outstream_end_write`.
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);
int soundio_outstream_write(struct SoundIoOutStream *outstream, int frame_count); // Commits the write that you began with `soundio_outstream_begin_write`.
// You must call this function only from `write_callback`.
int soundio_outstream_end_write(struct SoundIoOutStream *outstream, int frame_count);
void soundio_outstream_clear_buffer(struct SoundIoOutStream *outstream); void soundio_outstream_clear_buffer(struct SoundIoOutStream *outstream);
@ -555,8 +566,8 @@ int soundio_outstream_pause(struct SoundIoOutStream *outstream, bool pause);
// Input Streams // Input Streams
// allocates memory and sets defaults. Next you should fill out the struct fields // Allocates memory and sets defaults. Next you should fill out the struct fields
// and then call soundio_instream_open // and then call `soundio_instream_open`.
struct SoundIoInStream *soundio_instream_create(struct SoundIoDevice *device); struct SoundIoInStream *soundio_instream_create(struct SoundIoDevice *device);
void soundio_instream_destroy(struct SoundIoInStream *instream); void soundio_instream_destroy(struct SoundIoInStream *instream);
@ -564,10 +575,23 @@ int soundio_instream_open(struct SoundIoInStream *instream);
int soundio_instream_start(struct SoundIoInStream *instream); int soundio_instream_start(struct SoundIoInStream *instream);
void soundio_instream_peek(struct SoundIoInStream *instream, // Call this function when you are ready to begin reading from the device
const char **data, int *out_frame_count); // buffer.
// this will drop all of the frames from when you called soundio_instream_peek // * `instream` - (in) The input stream you want to read from.
void soundio_instream_drop(struct SoundIoInStream *instream); // * `areas` - (out) The memory addresses you can read data from. It is OK
// to modify the pointers if that helps you iterate.
// * `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`.
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`.
int soundio_instream_end_read(struct SoundIoInStream *instream);
void soundio_instream_clear_buffer(struct SoundIoInStream *instream); void soundio_instream_clear_buffer(struct SoundIoInStream *instream);
@ -579,25 +603,28 @@ int soundio_instream_pause(struct SoundIoInStream *instream, bool pause);
// Ring Buffer // Ring Buffer
struct SoundIoRingBuffer; struct SoundIoRingBuffer;
// `requested_capacity` in bytes.
struct SoundIoRingBuffer *soundio_ring_buffer_create(struct SoundIo *soundio, int requested_capacity); struct SoundIoRingBuffer *soundio_ring_buffer_create(struct SoundIo *soundio, int requested_capacity);
void soundio_ring_buffer_destroy(struct SoundIoRingBuffer *ring_buffer); void soundio_ring_buffer_destroy(struct SoundIoRingBuffer *ring_buffer);
int soundio_ring_buffer_capacity(struct SoundIoRingBuffer *ring_buffer); int soundio_ring_buffer_capacity(struct SoundIoRingBuffer *ring_buffer);
// don't write more than capacity // don't write more than capacity
char *soundio_ring_buffer_write_ptr(struct SoundIoRingBuffer *ring_buffer); char *soundio_ring_buffer_write_ptr(struct SoundIoRingBuffer *ring_buffer);
// `count` in bytes.
void soundio_ring_buffer_advance_write_ptr(struct SoundIoRingBuffer *ring_buffer, int count); void soundio_ring_buffer_advance_write_ptr(struct SoundIoRingBuffer *ring_buffer, int count);
// don't read more than capacity // don't read more than capacity
char *soundio_ring_buffer_read_ptr(struct SoundIoRingBuffer *ring_buffer); char *soundio_ring_buffer_read_ptr(struct SoundIoRingBuffer *ring_buffer);
// `count` in bytes.
void soundio_ring_buffer_advance_read_ptr(struct SoundIoRingBuffer *ring_buffer, int count); void soundio_ring_buffer_advance_read_ptr(struct SoundIoRingBuffer *ring_buffer, int count);
// how much of the buffer is used, ready for reading // Returns how many bytes of the buffer is used, ready for reading.
int soundio_ring_buffer_fill_count(struct SoundIoRingBuffer *ring_buffer); int soundio_ring_buffer_fill_count(struct SoundIoRingBuffer *ring_buffer);
// how much is available, ready for writing // Returns how many bytes of the buffer is free, ready for writing.
int soundio_ring_buffer_free_count(struct SoundIoRingBuffer *ring_buffer); int soundio_ring_buffer_free_count(struct SoundIoRingBuffer *ring_buffer);
// must be called by the writer // Must be called by the writer.
void soundio_ring_buffer_clear(struct SoundIoRingBuffer *ring_buffer); void soundio_ring_buffer_clear(struct SoundIoRingBuffer *ring_buffer);

View file

@ -34,7 +34,7 @@ struct SoundIoPrivate {
enum SoundIoBackend current_backend; enum SoundIoBackend current_backend;
// safe to read without a mutex from a single thread // Safe to read from a single thread without a mutex.
struct SoundIoDevicesInfo *safe_devices_info; struct SoundIoDevicesInfo *safe_devices_info;
void *backend_data; void *backend_data;
@ -49,7 +49,7 @@ struct SoundIoPrivate {
int (*outstream_free_count)(struct SoundIoPrivate *, struct SoundIoOutStreamPrivate *); int (*outstream_free_count)(struct SoundIoPrivate *, struct SoundIoOutStreamPrivate *);
int (*outstream_begin_write)(struct SoundIoPrivate *, struct SoundIoOutStreamPrivate *, int (*outstream_begin_write)(struct SoundIoPrivate *, struct SoundIoOutStreamPrivate *,
SoundIoChannelArea **out_areas, int *frame_count); SoundIoChannelArea **out_areas, int *frame_count);
int (*outstream_write)(struct SoundIoPrivate *, struct SoundIoOutStreamPrivate *, int frame_count); int (*outstream_end_write)(struct SoundIoPrivate *, struct SoundIoOutStreamPrivate *, int frame_count);
void (*outstream_clear_buffer)(struct SoundIoPrivate *, struct SoundIoOutStreamPrivate *); void (*outstream_clear_buffer)(struct SoundIoPrivate *, struct SoundIoOutStreamPrivate *);
int (*outstream_pause)(struct SoundIoPrivate *, struct SoundIoOutStreamPrivate *, bool pause); int (*outstream_pause)(struct SoundIoPrivate *, struct SoundIoOutStreamPrivate *, bool pause);
@ -57,9 +57,9 @@ struct SoundIoPrivate {
int (*instream_open)(struct SoundIoPrivate *, struct SoundIoInStreamPrivate *); int (*instream_open)(struct SoundIoPrivate *, struct SoundIoInStreamPrivate *);
void (*instream_destroy)(struct SoundIoPrivate *, struct SoundIoInStreamPrivate *); void (*instream_destroy)(struct SoundIoPrivate *, struct SoundIoInStreamPrivate *);
int (*instream_start)(struct SoundIoPrivate *, struct SoundIoInStreamPrivate *); int (*instream_start)(struct SoundIoPrivate *, struct SoundIoInStreamPrivate *);
void (*instream_peek)(struct SoundIoPrivate *, struct SoundIoInStreamPrivate *, int (*instream_begin_read)(struct SoundIoPrivate *, struct SoundIoInStreamPrivate *,
const char **data, int *frame_count); SoundIoChannelArea **out_areas, int *frame_count);
void (*instream_drop)(struct SoundIoPrivate *, struct SoundIoInStreamPrivate *); int (*instream_end_read)(struct SoundIoPrivate *, struct SoundIoInStreamPrivate *);
void (*instream_clear_buffer)(struct SoundIoPrivate *, struct SoundIoInStreamPrivate *); void (*instream_clear_buffer)(struct SoundIoPrivate *, struct SoundIoInStreamPrivate *);
int (*instream_pause)(struct SoundIoPrivate *, struct SoundIoInStreamPrivate *, bool pause); int (*instream_pause)(struct SoundIoPrivate *, struct SoundIoInStreamPrivate *, bool pause);
}; };