mirror of
https://github.com/Ryujinx/libsoundio.git
synced 2025-01-03 19:35:37 +00:00
microphone example working with PulseAudio
This commit is contained in:
parent
a3388e792a
commit
e7dc300cc6
|
@ -238,7 +238,6 @@ view `coverage/index.html` in a browser.
|
|||
|
||||
## 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 dummy linux, osx, windows
|
||||
0. implement JACK backend, get examples working
|
||||
|
|
|
@ -47,12 +47,93 @@ static void panic(const char *format, ...) {
|
|||
abort();
|
||||
}
|
||||
|
||||
static void read_callback(struct SoundIoInStream *instream) {
|
||||
fprintf(stderr, "read_callback\n");
|
||||
struct SoundIoRingBuffer *ring_buffer = NULL;
|
||||
|
||||
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) {
|
||||
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) {
|
||||
|
@ -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) {
|
||||
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();
|
||||
if (!soundio)
|
||||
panic("out of memory");
|
||||
|
||||
int err;
|
||||
if ((err = soundio_connect(soundio)))
|
||||
panic("error connecting: %s", soundio_strerror(err));
|
||||
int err = (backend == SoundIoBackendNone) ?
|
||||
soundio_connect(soundio) : soundio_connect_backend(soundio, backend);
|
||||
|
||||
int default_out_device_index = soundio_get_default_output_device_index(soundio);
|
||||
if (default_out_device_index < 0)
|
||||
|
@ -125,7 +224,7 @@ int main(int argc, char **argv) {
|
|||
instream->format = *fmt;
|
||||
instream->sample_rate = sample_rate;
|
||||
instream->layout = *layout;
|
||||
instream->buffer_duration = 0.1;
|
||||
instream->period_duration = 0.1;
|
||||
instream->read_callback = read_callback;
|
||||
|
||||
if ((err = soundio_instream_open(instream)))
|
||||
|
@ -137,13 +236,19 @@ int main(int argc, char **argv) {
|
|||
outstream->format = *fmt;
|
||||
outstream->sample_rate = sample_rate;
|
||||
outstream->layout = *layout;
|
||||
outstream->buffer_duration = 0.1;
|
||||
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));
|
||||
|
||||
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)))
|
||||
panic("unable to start input device: %s", soundio_strerror(err));
|
||||
|
||||
|
|
|
@ -61,7 +61,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;
|
||||
|
|
14
src/alsa.cpp
14
src/alsa.cpp
|
@ -1252,7 +1252,7 @@ int outstream_begin_write_alsa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os,
|
|||
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;
|
||||
SoundIoOutStream *outstream = &os->pub;
|
||||
|
||||
|
@ -1305,13 +1305,13 @@ static int instream_start_alsa(SoundIoPrivate *si, SoundIoInStreamPrivate *is) {
|
|||
soundio_panic("TODO");
|
||||
}
|
||||
|
||||
static void instream_peek_alsa(SoundIoPrivate *si,
|
||||
SoundIoInStreamPrivate *is, const char **data, int *frame_count)
|
||||
static int instream_begin_read_alsa(SoundIoPrivate *si,
|
||||
SoundIoInStreamPrivate *is, SoundIoChannelArea **out_areas, int *frame_count)
|
||||
{
|
||||
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");
|
||||
}
|
||||
|
||||
|
@ -1411,15 +1411,15 @@ int soundio_alsa_init(SoundIoPrivate *si) {
|
|||
si->outstream_start = outstream_start_alsa;
|
||||
si->outstream_free_count = outstream_free_count_alsa;
|
||||
si->outstream_begin_write = outstream_begin_write_alsa;
|
||||
si->outstream_write = outstream_write_alsa;
|
||||
si->outstream_end_write = outstream_end_write_alsa;
|
||||
si->outstream_clear_buffer = outstream_clear_buffer_alsa;
|
||||
si->outstream_pause = outstream_pause_alsa;
|
||||
|
||||
si->instream_open = instream_open_alsa;
|
||||
si->instream_destroy = instream_destroy_alsa;
|
||||
si->instream_start = instream_start_alsa;
|
||||
si->instream_peek = instream_peek_alsa;
|
||||
si->instream_drop = instream_drop_alsa;
|
||||
si->instream_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;
|
||||
|
||||
|
|
|
@ -231,7 +231,7 @@ static int outstream_begin_write_dummy(SoundIoPrivate *si,
|
|||
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;
|
||||
SoundIoOutStream *outstream = &os->pub;
|
||||
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");
|
||||
}
|
||||
|
||||
static void instream_peek_dummy(SoundIoPrivate *si,
|
||||
SoundIoInStreamPrivate *is, const char **data, int *frame_count)
|
||||
static int instream_begin_read_dummy(SoundIoPrivate *si,
|
||||
SoundIoInStreamPrivate *is, SoundIoChannelArea **out_areas, int *frame_count)
|
||||
{
|
||||
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");
|
||||
}
|
||||
|
||||
|
@ -460,15 +460,15 @@ int soundio_dummy_init(SoundIoPrivate *si) {
|
|||
si->outstream_start = outstream_start_dummy;
|
||||
si->outstream_free_count = outstream_free_count_dummy;
|
||||
si->outstream_begin_write = outstream_begin_write_dummy;
|
||||
si->outstream_write = outstream_write_dummy;
|
||||
si->outstream_end_write = outstream_end_write_dummy;
|
||||
si->outstream_clear_buffer = outstream_clear_buffer_dummy;
|
||||
si->outstream_pause = outstream_pause_dummy;
|
||||
|
||||
si->instream_open = instream_open_dummy;
|
||||
si->instream_destroy = instream_destroy_dummy;
|
||||
si->instream_start = instream_start_dummy;
|
||||
si->instream_peek = instream_peek_dummy;
|
||||
si->instream_drop = instream_drop_dummy;
|
||||
si->instream_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;
|
||||
|
||||
|
|
|
@ -19,14 +19,15 @@ struct SoundIoOutStreamPulseAudio {
|
|||
pa_stream *stream;
|
||||
atomic_bool stream_ready;
|
||||
pa_buffer_attr buffer_attr;
|
||||
SoundIoChannelArea areas[SOUNDIO_MAX_CHANNELS];
|
||||
char *write_ptr;
|
||||
SoundIoChannelArea areas[SOUNDIO_MAX_CHANNELS];
|
||||
};
|
||||
|
||||
struct SoundIoInStreamPulseAudio {
|
||||
pa_stream *stream;
|
||||
atomic_bool stream_ready;
|
||||
pa_buffer_attr buffer_attr;
|
||||
SoundIoChannelArea areas[SOUNDIO_MAX_CHANNELS];
|
||||
};
|
||||
|
||||
struct SoundIoPulseAudio {
|
||||
|
@ -730,6 +731,10 @@ static int outstream_start_pa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os) {
|
|||
while (!ospa->stream_ready)
|
||||
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);
|
||||
|
||||
return 0;
|
||||
|
@ -765,11 +770,13 @@ static int outstream_begin_write_pa(SoundIoPrivate *si,
|
|||
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;
|
||||
SoundIoOutStreamPulseAudio *ospa = (SoundIoOutStreamPulseAudio *)os->backend_data;
|
||||
pa_stream *stream = ospa->stream;
|
||||
assert(frame_count > 0);
|
||||
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))
|
||||
return SoundIoErrorStreaming;
|
||||
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) {
|
||||
SoundIoInStreamPrivate *is = (SoundIoInStreamPrivate*)userdata;
|
||||
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)) {
|
||||
case PA_STREAM_UNCONNECTED:
|
||||
case PA_STREAM_CREATING:
|
||||
|
@ -817,6 +828,7 @@ static void recording_stream_state_callback(pa_stream *stream, void *userdata) {
|
|||
break;
|
||||
case PA_STREAM_READY:
|
||||
ispa->stream_ready = true;
|
||||
pa_threaded_mainloop_signal(sipa->main_loop, 0);
|
||||
break;
|
||||
case PA_STREAM_FAILED:
|
||||
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) {
|
||||
SoundIoInStreamPrivate *is = (SoundIoInStreamPrivate*)userdata;
|
||||
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) {
|
||||
|
@ -924,32 +939,51 @@ static int instream_start_pa(SoundIoPrivate *si, SoundIoInStreamPrivate *is) {
|
|||
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);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void instream_peek_pa(SoundIoPrivate *si,
|
||||
SoundIoInStreamPrivate *is, const char **data, int *frame_count)
|
||||
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;
|
||||
if (ispa->stream_ready) {
|
||||
size_t nbytes;
|
||||
if (pa_stream_peek(stream, (const void **)data, &nbytes))
|
||||
soundio_panic("pa_stream_peek error: %s", pa_strerror(pa_context_errno(pa_stream_get_context(stream))));
|
||||
*frame_count = ((int)nbytes) / instream->bytes_per_frame;
|
||||
} else {
|
||||
*data = nullptr;
|
||||
*frame_count = 0;
|
||||
}
|
||||
|
||||
assert(ispa->stream_ready);
|
||||
|
||||
char *data;
|
||||
size_t nbytes = *frame_count * instream->bytes_per_frame;
|
||||
if (pa_stream_peek(stream, (const void **)&data, &nbytes))
|
||||
return SoundIoErrorStreaming;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
static void instream_drop_pa(SoundIoPrivate *si, SoundIoInStreamPrivate *is) {
|
||||
*frame_count = nbytes / instream->bytes_per_frame;
|
||||
*out_areas = ispa->areas;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int instream_end_read_pa(SoundIoPrivate *si, SoundIoInStreamPrivate *is) {
|
||||
SoundIoInStreamPulseAudio *ispa = (SoundIoInStreamPulseAudio *)is->backend_data;
|
||||
pa_stream *stream = ispa->stream;
|
||||
if (pa_stream_drop(stream))
|
||||
soundio_panic("pa_stream_drop error: %s", pa_strerror(pa_context_errno(pa_stream_get_context(stream))));
|
||||
return SoundIoErrorStreaming;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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_free_count = outstream_free_count_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_pause = outstream_pause_pa;
|
||||
|
||||
si->instream_open = instream_open_pa;
|
||||
si->instream_destroy = instream_destroy_pa;
|
||||
si->instream_start = instream_start_pa;
|
||||
si->instream_peek = instream_peek_pa;
|
||||
si->instream_drop = instream_drop_pa;
|
||||
si->instream_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;
|
||||
|
||||
|
|
|
@ -218,14 +218,14 @@ void soundio_disconnect(struct SoundIo *soundio) {
|
|||
si->outstream_start = nullptr;
|
||||
si->outstream_free_count = nullptr;
|
||||
si->outstream_begin_write = nullptr;
|
||||
si->outstream_write = nullptr;
|
||||
si->outstream_end_write = nullptr;
|
||||
si->outstream_clear_buffer = nullptr;
|
||||
|
||||
si->instream_open = nullptr;
|
||||
si->instream_destroy = nullptr;
|
||||
si->instream_start = nullptr;
|
||||
si->instream_peek = nullptr;
|
||||
si->instream_drop = nullptr;
|
||||
si->instream_begin_read = nullptr;
|
||||
si->instream_end_read = 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);
|
||||
}
|
||||
}
|
||||
soundio_outstream_write(outstream, frame_count);
|
||||
soundio_outstream_end_write(outstream, frame_count);
|
||||
requested_frame_count -= frame_count;
|
||||
}
|
||||
return 0;
|
||||
|
@ -365,11 +365,11 @@ int soundio_outstream_begin_write(struct SoundIoOutStream *outstream,
|
|||
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;
|
||||
SoundIoPrivate *si = (SoundIoPrivate *)soundio;
|
||||
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);
|
||||
}
|
||||
|
||||
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) {
|
||||
if (!devices_info)
|
||||
return;
|
||||
|
|
|
@ -249,6 +249,7 @@ struct SoundIoDevice {
|
|||
int format_count;
|
||||
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.
|
||||
// If sample rate information is missing due to a probe error, the field
|
||||
// will be set to zero.
|
||||
|
@ -300,6 +301,7 @@ struct SoundIoOutStream {
|
|||
// Defaults to SoundIoFormatFloat32NE, followed by the first one supported.
|
||||
enum SoundIoFormat format;
|
||||
|
||||
// Sample rate is the number of frames per second.
|
||||
// Defaults to 48000 (and then clamped into range).
|
||||
int sample_rate;
|
||||
|
||||
|
@ -357,6 +359,7 @@ struct SoundIoInStream {
|
|||
// Defaults to SoundIoFormatFloat32NE, followed by the first one supported.
|
||||
enum SoundIoFormat format;
|
||||
|
||||
// Sample rate is the number of frames per second.
|
||||
// Defaults to max(sample_rate_min, min(sample_rate_max, 48000))
|
||||
int sample_rate;
|
||||
|
||||
|
@ -375,7 +378,7 @@ struct SoundIoInStream {
|
|||
|
||||
// Defaults to NULL. Put whatever you want here.
|
||||
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".
|
||||
const char *name;
|
||||
|
@ -404,9 +407,10 @@ void soundio_disconnect(struct SoundIo *soundio);
|
|||
const char *soundio_strerror(int error);
|
||||
const char *soundio_backend_name(enum SoundIoBackend backend);
|
||||
|
||||
// return the number of available backends
|
||||
// Returns the number of available backends.
|
||||
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);
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
// Sample rate is the number of frames per second.
|
||||
static inline int soundio_get_bytes_per_second(enum SoundIoFormat format,
|
||||
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_output_device_count(struct SoundIo *soundio);
|
||||
|
||||
// returns NULL on error
|
||||
// call soundio_device_unref when you no longer have a reference to the pointer.
|
||||
// Always returns a device. Call soundio_device_unref when done.
|
||||
// `index` must be 0 <= index < soundio_get_input_device_count
|
||||
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);
|
||||
|
||||
// returns the index of the default input device
|
||||
|
@ -514,8 +521,8 @@ bool soundio_device_supports_layout(struct SoundIoDevice *device,
|
|||
|
||||
|
||||
// Output Streams
|
||||
// allocates memory and sets defaults. Next you should fill out the struct fields
|
||||
// and then call soundio_outstream_open
|
||||
// Allocates memory and sets defaults. Next you should fill out the struct fields
|
||||
// and then call `soundio_outstream_open`.
|
||||
struct SoundIoOutStream *soundio_outstream_create(struct SoundIoDevice *device);
|
||||
|
||||
int soundio_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
|
||||
// correct number of times as determined by `requested_frame_count` from
|
||||
// `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,
|
||||
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);
|
||||
|
||||
|
@ -555,8 +566,8 @@ int soundio_outstream_pause(struct SoundIoOutStream *outstream, bool pause);
|
|||
|
||||
|
||||
// Input Streams
|
||||
// allocates memory and sets defaults. Next you should fill out the struct fields
|
||||
// and then call soundio_instream_open
|
||||
// Allocates memory and sets defaults. Next you should fill out the struct fields
|
||||
// and then call `soundio_instream_open`.
|
||||
struct SoundIoInStream *soundio_instream_create(struct SoundIoDevice *device);
|
||||
void soundio_instream_destroy(struct SoundIoInStream *instream);
|
||||
|
||||
|
@ -564,10 +575,23 @@ int soundio_instream_open(struct SoundIoInStream *instream);
|
|||
|
||||
int soundio_instream_start(struct SoundIoInStream *instream);
|
||||
|
||||
void soundio_instream_peek(struct SoundIoInStream *instream,
|
||||
const char **data, int *out_frame_count);
|
||||
// this will drop all of the frames from when you called soundio_instream_peek
|
||||
void soundio_instream_drop(struct SoundIoInStream *instream);
|
||||
// Call this function when you are ready to begin reading from the device
|
||||
// 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.
|
||||
// * `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);
|
||||
|
||||
|
@ -579,25 +603,28 @@ int soundio_instream_pause(struct SoundIoInStream *instream, bool pause);
|
|||
|
||||
// Ring Buffer
|
||||
struct SoundIoRingBuffer;
|
||||
// `requested_capacity` in bytes.
|
||||
struct SoundIoRingBuffer *soundio_ring_buffer_create(struct SoundIo *soundio, int requested_capacity);
|
||||
void soundio_ring_buffer_destroy(struct SoundIoRingBuffer *ring_buffer);
|
||||
int soundio_ring_buffer_capacity(struct SoundIoRingBuffer *ring_buffer);
|
||||
|
||||
// don't write more than capacity
|
||||
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);
|
||||
|
||||
// don't read more than capacity
|
||||
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);
|
||||
|
||||
// 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);
|
||||
|
||||
// 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);
|
||||
|
||||
// must be called by the writer
|
||||
// Must be called by the writer.
|
||||
void soundio_ring_buffer_clear(struct SoundIoRingBuffer *ring_buffer);
|
||||
|
||||
|
||||
|
|
|
@ -34,7 +34,7 @@ struct SoundIoPrivate {
|
|||
|
||||
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;
|
||||
|
||||
void *backend_data;
|
||||
|
@ -49,7 +49,7 @@ struct SoundIoPrivate {
|
|||
int (*outstream_free_count)(struct SoundIoPrivate *, struct SoundIoOutStreamPrivate *);
|
||||
int (*outstream_begin_write)(struct SoundIoPrivate *, struct SoundIoOutStreamPrivate *,
|
||||
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 *);
|
||||
int (*outstream_pause)(struct SoundIoPrivate *, struct SoundIoOutStreamPrivate *, bool pause);
|
||||
|
||||
|
@ -57,9 +57,9 @@ struct SoundIoPrivate {
|
|||
int (*instream_open)(struct SoundIoPrivate *, struct SoundIoInStreamPrivate *);
|
||||
void (*instream_destroy)(struct SoundIoPrivate *, struct SoundIoInStreamPrivate *);
|
||||
int (*instream_start)(struct SoundIoPrivate *, struct SoundIoInStreamPrivate *);
|
||||
void (*instream_peek)(struct SoundIoPrivate *, struct SoundIoInStreamPrivate *,
|
||||
const char **data, int *frame_count);
|
||||
void (*instream_drop)(struct SoundIoPrivate *, struct SoundIoInStreamPrivate *);
|
||||
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);
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue