mirror of
https://github.com/Ryujinx/libsoundio.git
synced 2024-12-22 18:25:35 +00:00
microphone example working with dummy backend
This commit is contained in:
parent
057f4ea07e
commit
69764e1afa
25
README.md
25
README.md
|
@ -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
|
||||
|
||||
|
|
|
@ -64,11 +64,17 @@ static void read_callback(struct SoundIoInStream *instream, int available_frame_
|
|||
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 (!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);
|
||||
areas[ch].ptr += areas[ch].step;
|
||||
write_ptr += instream->bytes_per_sample;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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));
|
||||
|
|
|
@ -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));
|
||||
|
|
16
src/alsa.cpp
16
src/alsa.cpp
|
@ -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;
|
||||
|
|
215
src/dummy.cpp
215
src/dummy.cpp
|
@ -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
|
||||
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) {
|
||||
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);
|
||||
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;
|
||||
}
|
||||
|
||||
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 void instream_destroy_dummy(SoundIoPrivate *si, SoundIoInStreamPrivate *is) {
|
||||
soundio_panic("TODO");
|
||||
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;
|
||||
|
||||
static void instream_clear_buffer_dummy(SoundIoPrivate *si, SoundIoInStreamPrivate *is) {
|
||||
soundio_panic("TODO");
|
||||
}
|
||||
if (isd->hole_size > 0) {
|
||||
isd->hole_size -= isd->read_frame_count;
|
||||
return 0;
|
||||
}
|
||||
|
||||
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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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,16 +953,24 @@ 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;
|
||||
|
||||
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;
|
||||
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;
|
||||
}
|
||||
|
||||
*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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in a new issue