write and read functions no longer allow setting frame count

CoreAudio and JACK both have this limitation
This commit is contained in:
Andrew Kelley 2015-08-04 00:56:03 -07:00
parent df0ca8a772
commit f87961275d
16 changed files with 193 additions and 193 deletions

View file

@ -23,7 +23,7 @@ behavior on every platform.
- [JACK](http://jackaudio.org/) - [JACK](http://jackaudio.org/)
- [PulseAudio](http://www.freedesktop.org/wiki/Software/PulseAudio/) - [PulseAudio](http://www.freedesktop.org/wiki/Software/PulseAudio/)
- [ALSA](http://www.alsa-project.org/) - [ALSA](http://www.alsa-project.org/)
- (planned) [CoreAudio](https://developer.apple.com/library/mac/documentation/MusicAudio/Conceptual/CoreAudioOverview/Introduction/Introduction.html) - [CoreAudio](https://developer.apple.com/library/mac/documentation/MusicAudio/Conceptual/CoreAudioOverview/Introduction/Introduction.html)
- Dummy (silence) - Dummy (silence)
- (planned) [WASAPI](https://msdn.microsoft.com/en-us/library/windows/desktop/dd371455%28v=vs.85%29.aspx) - (planned) [WASAPI](https://msdn.microsoft.com/en-us/library/windows/desktop/dd371455%28v=vs.85%29.aspx)
- (planned) [ASIO](http://www.asio4all.com/) - (planned) [ASIO](http://www.asio4all.com/)
@ -242,6 +242,9 @@ view `coverage/index.html` in a browser.
## Roadmap ## Roadmap
0. implement CoreAudio (OSX) backend, get examples working 0. implement CoreAudio (OSX) backend, get examples working
- microphone example
- underflow example
0. ALSA backend for microphone example is broken
0. Add some builtin channel layouts from 0. Add some builtin channel layouts from
https://developer.apple.com/library/mac/documentation/MusicAudio/Reference/CoreAudioDataTypesRef/#//apple_ref/doc/constant_group/Audio_Channel_Layout_Tags https://developer.apple.com/library/mac/documentation/MusicAudio/Reference/CoreAudioDataTypesRef/#//apple_ref/doc/constant_group/Audio_Channel_Layout_Tags
0. Make sure sending bogus device id results in "SoundIoErrorNoSuchDevice" on each backend 0. Make sure sending bogus device id results in "SoundIoErrorNoSuchDevice" on each backend
@ -251,6 +254,7 @@ view `coverage/index.html` in a browser.
should do the same. should do the same.
0. implement WASAPI (Windows) backend, get examples working 0. implement WASAPI (Windows) backend, get examples working
0. implement ASIO (Windows) backend, get examples working 0. implement ASIO (Windows) backend, get examples working
0. Do we really want `period_duration` in the API?
0. Integrate into libgroove and test with Groove Basin 0. Integrate into libgroove and test with Groove Basin
0. clear buffer maybe could take an argument to say how many frames to not clear 0. clear buffer maybe could take an argument to say how many frames to not clear
0. Verify that JACK xrun callback context is the same as process callback. 0. Verify that JACK xrun callback context is the same as process callback.
@ -275,7 +279,7 @@ view `coverage/index.html` in a browser.
0. -fvisibility=hidden and then explicitly export stuff, or 0. -fvisibility=hidden and then explicitly export stuff, or
explicitly make the unexported stuff private explicitly make the unexported stuff private
0. add len arguments to APIs that have char * 0. add len arguments to APIs that have char *
- replace strdup with soundio_str_dupe - replace strdup with `soundio_str_dupe`
0. Support PulseAudio proplist properties for main context and streams 0. Support PulseAudio proplist properties for main context and streams
0. Expose JACK options in `jack_client_open` 0. Expose JACK options in `jack_client_open`
0. custom allocator support 0. custom allocator support

View file

@ -51,26 +51,24 @@ static void panic(const char *format, ...) {
struct SoundIoRingBuffer *ring_buffer = NULL; struct SoundIoRingBuffer *ring_buffer = NULL;
static int min_int(int a, int b) {
return (a < b) ? a : b;
}
static void read_callback(struct SoundIoInStream *instream, int available_frame_count) { static void read_callback(struct SoundIoInStream *instream, int available_frame_count) {
struct SoundIoChannelArea *areas;
int frame_count;
int err; int err;
char *write_ptr = soundio_ring_buffer_write_ptr(ring_buffer); char *write_ptr = soundio_ring_buffer_write_ptr(ring_buffer);
int free_bytes = soundio_ring_buffer_free_count(ring_buffer); int free_bytes = soundio_ring_buffer_free_count(ring_buffer);
int free_count = free_bytes / instream->bytes_per_frame; int free_count = free_bytes / instream->bytes_per_frame;
int write_count = min_int(available_frame_count, free_count);
int frames_left = write_count; //fprintf(stderr, "read_callback %d free %d\n", available_frame_count, free_count);
if (available_frame_count > free_count)
panic("ring buffer overflow");
for (;;) { for (;;) {
int frame_count = frames_left;
struct SoundIoChannelArea *areas;
if ((err = soundio_instream_begin_read(instream, &areas, &frame_count))) if ((err = soundio_instream_begin_read(instream, &areas, &frame_count)))
panic("begin read error: %s", soundio_strerror(err)); panic("begin read error: %s", soundio_strerror(err));
if (frame_count <= 0) if (!frame_count)
break; break;
if (!areas) { if (!areas) {
@ -90,36 +88,42 @@ static void read_callback(struct SoundIoInStream *instream, int available_frame_
if ((err = soundio_instream_end_read(instream))) if ((err = soundio_instream_end_read(instream)))
panic("end read error: %s", soundio_strerror(err)); panic("end read error: %s", soundio_strerror(err));
frames_left -= frame_count;
if (frames_left <= 0)
break;
} }
int advance_bytes = write_count * instream->bytes_per_frame; int advance_bytes = available_frame_count * instream->bytes_per_frame;
soundio_ring_buffer_advance_write_ptr(ring_buffer, advance_bytes); soundio_ring_buffer_advance_write_ptr(ring_buffer, advance_bytes);
int dropped_frames = available_frame_count - write_count;
if (dropped_frames > 0)
fprintf(stderr, "Dropped %d frames due to overflow\n", dropped_frames);
} }
static void write_callback(struct SoundIoOutStream *outstream, int requested_frame_count) { static void write_callback(struct SoundIoOutStream *outstream, int requested_frame_count) {
int err;
struct SoundIoChannelArea *areas; struct SoundIoChannelArea *areas;
int frame_count;
int err;
char *read_ptr = soundio_ring_buffer_read_ptr(ring_buffer); char *read_ptr = soundio_ring_buffer_read_ptr(ring_buffer);
int fill_bytes = soundio_ring_buffer_fill_count(ring_buffer); int fill_bytes = soundio_ring_buffer_fill_count(ring_buffer);
int fill_count = fill_bytes / outstream->bytes_per_frame; int fill_count = fill_bytes / outstream->bytes_per_frame;
int read_frames = min_int(requested_frame_count, fill_count);
int frames_left = read_frames; //fprintf(stderr, "write_callback %d fill %d\n", requested_frame_count, fill_count);
if (requested_frame_count > fill_count) {
// Ring buffer does not have enough data, fill with zeroes.
for (;;) {
if ((err = soundio_outstream_begin_write(outstream, &areas, &frame_count)))
panic("begin write error: %s", soundio_strerror(err));
if (frame_count <= 0)
return;
for (int frame = 0; frame < 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;
}
}
if ((err = soundio_outstream_end_write(outstream)))
panic("end write error: %s", soundio_strerror(err));
}
}
for (;;) { for (;;) {
int frame_count = frames_left;
if (frame_count <= 0)
break;
if ((err = soundio_outstream_begin_write(outstream, &areas, &frame_count))) if ((err = soundio_outstream_begin_write(outstream, &areas, &frame_count)))
panic("begin write error: %s", soundio_strerror(err)); panic("begin write error: %s", soundio_strerror(err));
@ -134,13 +138,11 @@ static void write_callback(struct SoundIoOutStream *outstream, int requested_fra
} }
} }
if ((err = soundio_outstream_end_write(outstream, frame_count))) if ((err = soundio_outstream_end_write(outstream)))
panic("end write error: %s", soundio_strerror(err)); panic("end write error: %s", soundio_strerror(err));
frames_left -= frame_count;
} }
soundio_ring_buffer_advance_read_ptr(ring_buffer, read_frames * outstream->bytes_per_frame); soundio_ring_buffer_advance_read_ptr(ring_buffer, requested_frame_count * outstream->bytes_per_frame);
} }
static void underflow_callback(struct SoundIoOutStream *outstream) { static void underflow_callback(struct SoundIoOutStream *outstream) {

View file

@ -35,11 +35,11 @@ static float seconds_offset = 0.0f;
static void write_callback(struct SoundIoOutStream *outstream, int requested_frame_count) { static void write_callback(struct SoundIoOutStream *outstream, int requested_frame_count) {
float float_sample_rate = outstream->sample_rate; float float_sample_rate = outstream->sample_rate;
float seconds_per_frame = 1.0f / float_sample_rate; float seconds_per_frame = 1.0f / float_sample_rate;
int frame_count;
int err; int err;
for (;;) {
int frame_count = requested_frame_count;
for (;;) {
struct SoundIoChannelArea *areas; struct SoundIoChannelArea *areas;
if ((err = soundio_outstream_begin_write(outstream, &areas, &frame_count))) if ((err = soundio_outstream_begin_write(outstream, &areas, &frame_count)))
panic("%s", soundio_strerror(err)); panic("%s", soundio_strerror(err));
@ -60,15 +60,11 @@ 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_end_write(outstream, frame_count))) { if ((err = soundio_outstream_end_write(outstream))) {
if (err == SoundIoErrorUnderflow) if (err == SoundIoErrorUnderflow)
return; return;
panic("%s", soundio_strerror(err)); panic("%s", soundio_strerror(err));
} }
requested_frame_count -= frame_count;
if (requested_frame_count <= 0)
break;
} }
} }

View file

@ -417,8 +417,11 @@ struct SoundIoOutStream {
// Defaults to NULL. Put whatever you want here. // Defaults to NULL. Put whatever you want here.
void *userdata; void *userdata;
// In this callback, you call `soundio_outstream_begin_write` and // In this callback, you call `soundio_outstream_begin_write` and
// `soundio_outstream_end_write`. `requested_frame_count` will always be // `soundio_outstream_end_write` as many times as necessary to write
// greater than 0. // exactly `requested_frame_count` frames. `requested_frame_count` will
// always be greater than 0. To be compatible with all backends, you must
// write exactly `requested_frame_count` frames during the callback,
// otherwise a buffer underrun will occur.
void (*write_callback)(struct SoundIoOutStream *, int requested_frame_count); void (*write_callback)(struct SoundIoOutStream *, int requested_frame_count);
// This optional callback happens when the sound device runs out of buffered // This optional callback happens when the sound device runs out of buffered
// audio data to play. After this occurs, the outstream waits until the // audio data to play. After this occurs, the outstream waits until the
@ -712,21 +715,20 @@ int soundio_outstream_start(struct SoundIoOutStream *outstream);
// * `outstream` - (in) The output stream you want to write to. // * `outstream` - (in) The output stream you want to write to.
// * `areas` - (out) The memory addresses you can write data to. It is OK to // * `areas` - (out) The memory addresses you can write data to. It is OK to
// modify the pointers if that helps you iterate. // modify the pointers if that helps you iterate.
// * `frame_count` - (in/out) Provide the number of frames you want to write. // * `frame_count` - (out) Returns the number of frames you actually can write.
// Returned will be the number of frames you actually can write. Must be
// greater than 0 frames.
// 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 sio_sine.c for an example. // `write_callback`. See sio_sine.c for an example.
// You must call this function only from the `write_callback` thread context. // You must call this function only from the `write_callback` thread context.
// After calling this function, write data to `areas` and then call `soundio_outstream_end_write`. // 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);
// Commits the write that you began with `soundio_outstream_begin_write`. // Commits the write that you began with `soundio_outstream_begin_write`.
// You must call this function only from the `write_callback` thread context. // You must call this function only from the `write_callback` thread context.
// This function might return `SoundIoErrorUnderflow` but don't count on it. // This function might return `SoundIoErrorUnderflow` but don't count on it.
int soundio_outstream_end_write(struct SoundIoOutStream *outstream, int frame_count); int soundio_outstream_end_write(struct SoundIoOutStream *outstream);
// Clears the output stream buffer. // Clears the output stream buffer.
// You must call this function only from the `write_callback` thread context. // You must call this function only from the `write_callback` thread context.
@ -760,12 +762,11 @@ int soundio_instream_start(struct SoundIoInStream *instream);
// buffer. // buffer.
// * `instream` - (in) The input stream you want to read from. // * `instream` - (in) The input stream you want to read from.
// * `areas` - (out) The memory addresses you can read data from. It is OK // * `areas` - (out) The memory addresses you can read data from. It is OK
// to modify the pointers if that helps you iterate. If a buffer overflow // to modify the pointers if that helps you iterate. There might be a "hole"
// occurred, there will be a "hole" in the buffer. To indicate this, // in the buffer. To indicate this, `areas` will be `NULL` and `frame_count`
// `areas` will be `NULL` and `frame_count` tells how big the hole is in // tells how big the hole is in frames.
// frames. // * `frame_count` - (out) - Returns the number of frames you can actually
// * `frame_count` - (in/out) - Provide the number of frames you want to read. // 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 // 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 // correct number of times as determined by `available_frame_count` from
// `read_callback`. See sio_microphone.c for an example. // `read_callback`. See sio_microphone.c for an example.

View file

@ -1013,6 +1013,7 @@ void outstream_thread_run(void *arg) {
continue; continue;
} }
osa->frames_left = avail;
outstream->write_callback(outstream, avail); outstream->write_callback(outstream, avail);
continue; continue;
} }
@ -1080,6 +1081,7 @@ static void instream_thread_run(void *arg) {
continue; continue;
} }
isa->frames_left = avail;
instream->read_callback(instream, avail); instream->read_callback(instream, avail);
continue; continue;
} }
@ -1283,7 +1285,7 @@ static int outstream_start_alsa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os)
} }
int outstream_begin_write_alsa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os, int outstream_begin_write_alsa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os,
struct SoundIoChannelArea **out_areas, int *frame_count) struct SoundIoChannelArea **out_areas, int *out_frame_count)
{ {
*out_areas = nullptr; *out_areas = nullptr;
SoundIoOutStreamAlsa *osa = &os->backend_data.alsa; SoundIoOutStreamAlsa *osa = &os->backend_data.alsa;
@ -1295,17 +1297,19 @@ int outstream_begin_write_alsa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os,
osa->areas[ch].step = outstream->bytes_per_frame; osa->areas[ch].step = outstream->bytes_per_frame;
} }
*frame_count = min(*frame_count, osa->period_size); osa->frames_to_write = min(osa->frames_left, osa->period_size);
*out_frame_count = osa->frames_to_write;
} else if (osa->access == SND_PCM_ACCESS_RW_NONINTERLEAVED) { } else if (osa->access == SND_PCM_ACCESS_RW_NONINTERLEAVED) {
for (int ch = 0; ch < outstream->layout.channel_count; ch += 1) { for (int ch = 0; ch < outstream->layout.channel_count; ch += 1) {
osa->areas[ch].ptr = osa->sample_buffer + ch * outstream->bytes_per_sample * osa->period_size; osa->areas[ch].ptr = osa->sample_buffer + ch * outstream->bytes_per_sample * osa->period_size;
osa->areas[ch].step = outstream->bytes_per_sample; osa->areas[ch].step = outstream->bytes_per_sample;
} }
*frame_count = min(*frame_count, osa->period_size); osa->frames_to_write = min(osa->frames_left, osa->period_size);
*out_frame_count = osa->frames_to_write;
} else { } else {
const snd_pcm_channel_area_t *areas; const snd_pcm_channel_area_t *areas;
snd_pcm_uframes_t frames = *frame_count; snd_pcm_uframes_t frames = osa->frames_left;
int err; int err;
if ((err = snd_pcm_mmap_begin(osa->handle, &areas, &osa->offset, &frames)) < 0) { if ((err = snd_pcm_mmap_begin(osa->handle, &areas, &osa->offset, &frames)) < 0) {
@ -1323,37 +1327,40 @@ int outstream_begin_write_alsa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os,
(osa->areas[ch].step * osa->offset); (osa->areas[ch].step * osa->offset);
} }
*frame_count = frames; osa->frames_to_write = frames;
*out_frame_count = osa->frames_to_write;
} }
*out_areas = osa->areas; *out_areas = osa->areas;
return 0; return 0;
} }
static int outstream_end_write_alsa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os, int frame_count) { static int outstream_end_write_alsa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os) {
SoundIoOutStreamAlsa *osa = &os->backend_data.alsa; SoundIoOutStreamAlsa *osa = &os->backend_data.alsa;
SoundIoOutStream *outstream = &os->pub; SoundIoOutStream *outstream = &os->pub;
snd_pcm_sframes_t commitres; snd_pcm_sframes_t commitres;
if (osa->access == SND_PCM_ACCESS_RW_INTERLEAVED) { if (osa->access == SND_PCM_ACCESS_RW_INTERLEAVED) {
commitres = snd_pcm_writei(osa->handle, osa->sample_buffer, frame_count); commitres = snd_pcm_writei(osa->handle, osa->sample_buffer, osa->frames_to_write);
} else if (osa->access == SND_PCM_ACCESS_RW_NONINTERLEAVED) { } else if (osa->access == SND_PCM_ACCESS_RW_NONINTERLEAVED) {
char *ptrs[SOUNDIO_MAX_CHANNELS]; char *ptrs[SOUNDIO_MAX_CHANNELS];
for (int ch = 0; ch < outstream->layout.channel_count; ch += 1) { for (int ch = 0; ch < outstream->layout.channel_count; ch += 1) {
ptrs[ch] = osa->sample_buffer + ch * outstream->bytes_per_sample * osa->period_size; ptrs[ch] = osa->sample_buffer + ch * outstream->bytes_per_sample * osa->period_size;
} }
commitres = snd_pcm_writen(osa->handle, (void**)ptrs, frame_count); commitres = snd_pcm_writen(osa->handle, (void**)ptrs, osa->frames_to_write);
} else { } else {
commitres = snd_pcm_mmap_commit(osa->handle, osa->offset, frame_count); commitres = snd_pcm_mmap_commit(osa->handle, osa->offset, osa->frames_to_write);
} }
if (commitres < 0 || commitres != frame_count) { if (commitres < 0 || commitres != osa->frames_to_write) {
int err = (commitres >= 0) ? -EPIPE : commitres; int err = (commitres >= 0) ? -EPIPE : commitres;
if (err == -EPIPE || err == -ESTRPIPE) if (err == -EPIPE || err == -ESTRPIPE)
return SoundIoErrorUnderflow; return SoundIoErrorUnderflow;
else else
return SoundIoErrorStreaming; return SoundIoErrorStreaming;
} }
osa->frames_left -= osa->frames_to_write;
assert(osa->frames_left >= 0);
return 0; return 0;
} }
@ -1560,7 +1567,7 @@ static int instream_start_alsa(SoundIoPrivate *si, SoundIoInStreamPrivate *is) {
} }
static int instream_begin_read_alsa(SoundIoPrivate *si, static int instream_begin_read_alsa(SoundIoPrivate *si,
SoundIoInStreamPrivate *is, SoundIoChannelArea **out_areas, int *frame_count) SoundIoInStreamPrivate *is, SoundIoChannelArea **out_areas, int *out_frame_count)
{ {
*out_areas = nullptr; *out_areas = nullptr;
SoundIoInStreamAlsa *isa = &is->backend_data.alsa; SoundIoInStreamAlsa *isa = &is->backend_data.alsa;
@ -1572,10 +1579,11 @@ static int instream_begin_read_alsa(SoundIoPrivate *si,
isa->areas[ch].step = instream->bytes_per_frame; isa->areas[ch].step = instream->bytes_per_frame;
} }
isa->read_frame_count = min(*frame_count, isa->period_size); isa->read_frame_count = min(isa->frames_left, isa->period_size);
*frame_count = isa->read_frame_count; *out_frame_count = isa->read_frame_count;
snd_pcm_sframes_t commitres = snd_pcm_readi(isa->handle, isa->sample_buffer, isa->read_frame_count); snd_pcm_sframes_t commitres = snd_pcm_readi(isa->handle, isa->sample_buffer, isa->read_frame_count);
if (commitres < 0 || commitres != *frame_count) { if (commitres < 0 || commitres != isa->read_frame_count) {
int err = (commitres >= 0) ? -EPIPE : commitres; int err = (commitres >= 0) ? -EPIPE : commitres;
if ((err = instream_xrun_recovery(is, err)) < 0) if ((err = instream_xrun_recovery(is, err)) < 0)
return SoundIoErrorStreaming; return SoundIoErrorStreaming;
@ -1588,17 +1596,18 @@ static int instream_begin_read_alsa(SoundIoPrivate *si,
ptrs[ch] = isa->areas[ch].ptr; ptrs[ch] = isa->areas[ch].ptr;
} }
isa->read_frame_count = min(*frame_count, isa->period_size); isa->read_frame_count = min(isa->frames_left, isa->period_size);
*frame_count = isa->read_frame_count; *out_frame_count = isa->read_frame_count;
snd_pcm_sframes_t commitres = snd_pcm_readn(isa->handle, (void**)ptrs, isa->read_frame_count); snd_pcm_sframes_t commitres = snd_pcm_readn(isa->handle, (void**)ptrs, isa->read_frame_count);
if (commitres < 0 || commitres != *frame_count) { if (commitres < 0 || commitres != isa->read_frame_count) {
int err = (commitres >= 0) ? -EPIPE : commitres; int err = (commitres >= 0) ? -EPIPE : commitres;
if ((err = instream_xrun_recovery(is, err)) < 0) if ((err = instream_xrun_recovery(is, err)) < 0)
return SoundIoErrorStreaming; return SoundIoErrorStreaming;
} }
} else { } else {
const snd_pcm_channel_area_t *areas; const snd_pcm_channel_area_t *areas;
snd_pcm_uframes_t frames = *frame_count; snd_pcm_uframes_t frames = isa->frames_left;
int err; int err;
if ((err = snd_pcm_mmap_begin(isa->handle, &areas, &isa->offset, &frames)) < 0) { if ((err = snd_pcm_mmap_begin(isa->handle, &areas, &isa->offset, &frames)) < 0) {
@ -1615,7 +1624,7 @@ static int instream_begin_read_alsa(SoundIoPrivate *si,
} }
isa->read_frame_count = frames; isa->read_frame_count = frames;
*frame_count = isa->read_frame_count; *out_frame_count = isa->read_frame_count;
} }
*out_areas = isa->areas; *out_areas = isa->areas;
@ -1626,9 +1635,9 @@ static int instream_end_read_alsa(SoundIoPrivate *si, SoundIoInStreamPrivate *is
SoundIoInStreamAlsa *isa = &is->backend_data.alsa; SoundIoInStreamAlsa *isa = &is->backend_data.alsa;
if (isa->access == SND_PCM_ACCESS_RW_INTERLEAVED) { if (isa->access == SND_PCM_ACCESS_RW_INTERLEAVED) {
return 0; // nothing to do
} else if (isa->access == SND_PCM_ACCESS_RW_NONINTERLEAVED) { } else if (isa->access == SND_PCM_ACCESS_RW_NONINTERLEAVED) {
return 0; // nothing to do
} else { } else {
snd_pcm_sframes_t commitres = snd_pcm_mmap_commit(isa->handle, isa->offset, isa->read_frame_count); snd_pcm_sframes_t commitres = snd_pcm_mmap_commit(isa->handle, isa->offset, isa->read_frame_count);
if (commitres < 0 || commitres != isa->read_frame_count) { if (commitres < 0 || commitres != isa->read_frame_count) {
@ -1638,6 +1647,8 @@ static int instream_end_read_alsa(SoundIoPrivate *si, SoundIoInStreamPrivate *is
} }
} }
isa->frames_left -= isa->read_frame_count;
assert(isa->frames_left >= 0);
return 0; return 0;
} }

View file

@ -49,6 +49,8 @@ struct SoundIoOutStreamAlsa {
SoundIoOsThread *thread; SoundIoOsThread *thread;
atomic_flag thread_exit_flag; atomic_flag thread_exit_flag;
int period_size; int period_size;
int frames_left;
int frames_to_write;
SoundIoChannelArea areas[SOUNDIO_MAX_CHANNELS]; SoundIoChannelArea areas[SOUNDIO_MAX_CHANNELS];
}; };
@ -65,6 +67,7 @@ struct SoundIoInStreamAlsa {
SoundIoOsThread *thread; SoundIoOsThread *thread;
atomic_flag thread_exit_flag; atomic_flag thread_exit_flag;
int period_size; int period_size;
int frames_left;
int read_frame_count; int read_frame_count;
SoundIoChannelArea areas[SOUNDIO_MAX_CHANNELS]; SoundIoChannelArea areas[SOUNDIO_MAX_CHANNELS];
}; };

View file

@ -865,8 +865,7 @@ static int outstream_begin_write_ca(struct SoundIoPrivate *si, struct SoundIoOut
return 0; return 0;
} }
static int outstream_end_write_ca(struct SoundIoPrivate *si, struct SoundIoOutStreamPrivate *os, int) static int outstream_end_write_ca(struct SoundIoPrivate *si, struct SoundIoOutStreamPrivate *os) {
{
SoundIoOutStreamCoreAudio *osca = &os->backend_data.coreaudio; SoundIoOutStreamCoreAudio *osca = &os->backend_data.coreaudio;
osca->buffer_index += 1; osca->buffer_index += 1;
return 0; return 0;

View file

@ -20,6 +20,7 @@ static void playback_thread_run(void *arg) {
int fill_bytes = soundio_ring_buffer_fill_count(&osd->ring_buffer); int fill_bytes = soundio_ring_buffer_fill_count(&osd->ring_buffer);
int free_bytes = soundio_ring_buffer_capacity(&osd->ring_buffer) - fill_bytes; int free_bytes = soundio_ring_buffer_capacity(&osd->ring_buffer) - fill_bytes;
int free_frames = free_bytes / outstream->bytes_per_frame; int free_frames = free_bytes / outstream->bytes_per_frame;
osd->frames_left = free_frames;
outstream->write_callback(outstream, free_frames); outstream->write_callback(outstream, free_frames);
double start_time = soundio_os_get_time(); double start_time = soundio_os_get_time();
long frames_consumed = 0; long frames_consumed = 0;
@ -47,10 +48,12 @@ static void playback_thread_run(void *arg) {
if (frames_to_kill > fill_frames) { if (frames_to_kill > fill_frames) {
outstream->underflow_callback(outstream); outstream->underflow_callback(outstream);
osd->frames_left = free_frames;
outstream->write_callback(outstream, free_frames); outstream->write_callback(outstream, free_frames);
frames_consumed = 0; frames_consumed = 0;
start_time = soundio_os_get_time(); start_time = soundio_os_get_time();
} else if (free_frames > 0) { } else if (free_frames > 0) {
osd->frames_left = free_frames;
outstream->write_callback(outstream, free_frames); outstream->write_callback(outstream, free_frames);
} }
} }
@ -90,6 +93,7 @@ static void capture_thread_run(void *arg) {
start_time = soundio_os_get_time(); start_time = soundio_os_get_time();
} }
if (fill_frames > 0) { if (fill_frames > 0) {
isd->frames_left = fill_frames;
instream->read_callback(instream, fill_frames); instream->read_callback(instream, fill_frames);
} }
} }
@ -197,37 +201,28 @@ static int outstream_start_dummy(SoundIoPrivate *si, SoundIoOutStreamPrivate *os
} }
static int outstream_begin_write_dummy(SoundIoPrivate *si, static int outstream_begin_write_dummy(SoundIoPrivate *si,
SoundIoOutStreamPrivate *os, SoundIoChannelArea **out_areas, int *frame_count) SoundIoOutStreamPrivate *os, SoundIoChannelArea **out_areas, int *out_frame_count)
{ {
*out_areas = nullptr;
SoundIoOutStream *outstream = &os->pub; SoundIoOutStream *outstream = &os->pub;
SoundIoOutStreamDummy *osd = &os->backend_data.dummy; SoundIoOutStreamDummy *osd = &os->backend_data.dummy;
assert(*frame_count >= 0); char *write_ptr = soundio_ring_buffer_write_ptr(&osd->ring_buffer);
assert(*frame_count <= osd->buffer_frame_count); for (int ch = 0; ch < outstream->layout.channel_count; ch += 1) {
osd->areas[ch].ptr = write_ptr + outstream->bytes_per_sample * ch;
int free_byte_count = soundio_ring_buffer_free_count(&osd->ring_buffer); osd->areas[ch].step = outstream->bytes_per_frame;
int free_frame_count = free_byte_count / outstream->bytes_per_frame;
*frame_count = min(*frame_count, free_frame_count);
if (free_frame_count) {
char *write_ptr = soundio_ring_buffer_write_ptr(&osd->ring_buffer);
for (int ch = 0; ch < outstream->layout.channel_count; ch += 1) {
osd->areas[ch].ptr = write_ptr + outstream->bytes_per_sample * ch;
osd->areas[ch].step = outstream->bytes_per_frame;
}
*out_areas = osd->areas;
} }
*out_frame_count = osd->frames_left;
*out_areas = osd->areas;
return 0; return 0;
} }
static int outstream_end_write_dummy(SoundIoPrivate *si, SoundIoOutStreamPrivate *os, int frame_count) { static int outstream_end_write_dummy(SoundIoPrivate *si, SoundIoOutStreamPrivate *os) {
SoundIoOutStreamDummy *osd = &os->backend_data.dummy; SoundIoOutStreamDummy *osd = &os->backend_data.dummy;
SoundIoOutStream *outstream = &os->pub; SoundIoOutStream *outstream = &os->pub;
int byte_count = frame_count * outstream->bytes_per_frame; int byte_count = osd->frames_left * outstream->bytes_per_frame;
soundio_ring_buffer_advance_write_ptr(&osd->ring_buffer, byte_count); soundio_ring_buffer_advance_write_ptr(&osd->ring_buffer, byte_count);
osd->frames_left = 0;
return 0; return 0;
} }
@ -310,37 +305,29 @@ static int instream_start_dummy(SoundIoPrivate *si, SoundIoInStreamPrivate *is)
} }
static int instream_begin_read_dummy(SoundIoPrivate *si, static int instream_begin_read_dummy(SoundIoPrivate *si,
SoundIoInStreamPrivate *is, SoundIoChannelArea **out_areas, int *frame_count) SoundIoInStreamPrivate *is, SoundIoChannelArea **out_areas, int *out_frame_count)
{ {
SoundIoInStream *instream = &is->pub; SoundIoInStream *instream = &is->pub;
SoundIoInStreamDummy *isd = &is->backend_data.dummy; SoundIoInStreamDummy *isd = &is->backend_data.dummy;
assert(*frame_count >= 0); char *read_ptr = soundio_ring_buffer_read_ptr(&isd->ring_buffer);
assert(*frame_count <= isd->buffer_frame_count); for (int ch = 0; ch < instream->layout.channel_count; ch += 1) {
isd->areas[ch].ptr = read_ptr + instream->bytes_per_sample * ch;
int fill_byte_count = soundio_ring_buffer_fill_count(&isd->ring_buffer); isd->areas[ch].step = instream->bytes_per_frame;
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;
} }
*out_frame_count = isd->frames_left;
*out_areas = isd->areas;
return 0; return 0;
} }
static int instream_end_read_dummy(SoundIoPrivate *si, SoundIoInStreamPrivate *is) { static int instream_end_read_dummy(SoundIoPrivate *si, SoundIoInStreamPrivate *is) {
SoundIoInStreamDummy *isd = &is->backend_data.dummy; SoundIoInStreamDummy *isd = &is->backend_data.dummy;
SoundIoInStream *instream = &is->pub; SoundIoInStream *instream = &is->pub;
int byte_count = isd->read_frame_count * instream->bytes_per_frame; int byte_count = isd->frames_left * instream->bytes_per_frame;
soundio_ring_buffer_advance_read_ptr(&isd->ring_buffer, byte_count); soundio_ring_buffer_advance_read_ptr(&isd->ring_buffer, byte_count);
isd->frames_left = 0;
return 0; return 0;
} }

View file

@ -30,6 +30,7 @@ struct SoundIoOutStreamDummy {
struct SoundIoOsCond *cond; struct SoundIoOsCond *cond;
atomic_flag abort_flag; atomic_flag abort_flag;
int buffer_frame_count; int buffer_frame_count;
int frames_left;
struct SoundIoRingBuffer ring_buffer; struct SoundIoRingBuffer ring_buffer;
double playback_start_time; double playback_start_time;
SoundIoChannelArea areas[SOUNDIO_MAX_CHANNELS]; SoundIoChannelArea areas[SOUNDIO_MAX_CHANNELS];
@ -39,7 +40,7 @@ struct SoundIoInStreamDummy {
struct SoundIoOsThread *thread; struct SoundIoOsThread *thread;
struct SoundIoOsCond *cond; struct SoundIoOsCond *cond;
atomic_flag abort_flag; atomic_flag abort_flag;
int read_frame_count; int frames_left;
int buffer_frame_count; int buffer_frame_count;
struct SoundIoRingBuffer ring_buffer; struct SoundIoRingBuffer ring_buffer;
SoundIoChannelArea areas[SOUNDIO_MAX_CHANNELS]; SoundIoChannelArea areas[SOUNDIO_MAX_CHANNELS];

View file

@ -342,7 +342,8 @@ static int outstream_process_callback(jack_nframes_t nframes, void *arg) {
osj->frames_left = nframes; osj->frames_left = nframes;
for (int ch = 0; ch < outstream->layout.channel_count; ch += 1) { for (int ch = 0; ch < outstream->layout.channel_count; ch += 1) {
SoundIoOutStreamJackPort *osjp = &osj->ports[ch]; SoundIoOutStreamJackPort *osjp = &osj->ports[ch];
osj->buf_ptrs[ch] = (char*)jack_port_get_buffer(osjp->source_port, osj->frames_left); osj->areas[ch].ptr = (char*)jack_port_get_buffer(osjp->source_port, nframes);
osj->areas[ch].step = outstream->bytes_per_sample;
} }
outstream->write_callback(outstream, osj->frames_left); outstream->write_callback(outstream, osj->frames_left);
return 0; return 0;
@ -525,37 +526,19 @@ static int outstream_start_jack(struct SoundIoPrivate *si, struct SoundIoOutStre
} }
static int outstream_begin_write_jack(struct SoundIoPrivate *si, struct SoundIoOutStreamPrivate *os, static int outstream_begin_write_jack(struct SoundIoPrivate *si, struct SoundIoOutStreamPrivate *os,
SoundIoChannelArea **out_areas, int *frame_count) SoundIoChannelArea **out_areas, int *out_frame_count)
{ {
SoundIoOutStream *outstream = &os->pub;
SoundIoOutStreamJack *osj = &os->backend_data.jack; SoundIoOutStreamJack *osj = &os->backend_data.jack;
*frame_count = min(*frame_count, osj->frames_left); *out_frame_count = osj->frames_left;
for (int ch = 0; ch < outstream->layout.channel_count; ch += 1) {
osj->areas[ch].ptr = osj->buf_ptrs[ch];
osj->areas[ch].step = outstream->bytes_per_sample;
}
*out_areas = osj->areas; *out_areas = osj->areas;
return 0; return 0;
} }
static int outstream_end_write_jack(struct SoundIoPrivate *si, struct SoundIoOutStreamPrivate *os, static int outstream_end_write_jack(struct SoundIoPrivate *si, struct SoundIoOutStreamPrivate *os) {
int frame_count)
{
SoundIoOutStream *outstream = &os->pub;
SoundIoOutStreamJack *osj = &os->backend_data.jack; SoundIoOutStreamJack *osj = &os->backend_data.jack;
assert(frame_count <= osj->frames_left); osj->frames_left = 0;
osj->frames_left -= frame_count;
if (osj->frames_left > 0) {
for (int ch = 0; ch < outstream->layout.channel_count; ch += 1) {
osj->buf_ptrs[ch] += frame_count * outstream->bytes_per_sample;
}
}
return 0; return 0;
} }
@ -611,7 +594,14 @@ static void instream_shutdown_callback(void *arg) {
static int instream_process_callback(jack_nframes_t nframes, void *arg) { static int instream_process_callback(jack_nframes_t nframes, void *arg) {
SoundIoInStreamPrivate *is = (SoundIoInStreamPrivate *)arg; SoundIoInStreamPrivate *is = (SoundIoInStreamPrivate *)arg;
SoundIoInStream *instream = &is->pub; SoundIoInStream *instream = &is->pub;
instream->read_callback(instream, nframes); SoundIoInStreamJack *isj = &is->backend_data.jack;
isj->frames_left = nframes;
for (int ch = 0; ch < instream->layout.channel_count; ch += 1) {
SoundIoInStreamJackPort *isjp = &isj->ports[ch];
isj->areas[ch].ptr = (char*)jack_port_get_buffer(isjp->dest_port, nframes);
isj->areas[ch].step = instream->bytes_per_sample;
}
instream->read_callback(instream, isj->frames_left);
return 0; return 0;
} }
@ -736,27 +726,19 @@ static int instream_start_jack(struct SoundIoPrivate *si, struct SoundIoInStream
} }
static int instream_begin_read_jack(struct SoundIoPrivate *si, struct SoundIoInStreamPrivate *is, static int instream_begin_read_jack(struct SoundIoPrivate *si, struct SoundIoInStreamPrivate *is,
SoundIoChannelArea **out_areas, int *frame_count) SoundIoChannelArea **out_areas, int *out_frame_count)
{ {
SoundIoInStream *instream = &is->pub;
SoundIoInStreamJack *isj = &is->backend_data.jack; SoundIoInStreamJack *isj = &is->backend_data.jack;
SoundIoJack *sij = &si->backend_data.jack;
assert(*frame_count <= sij->period_size);
for (int ch = 0; ch < instream->layout.channel_count; ch += 1) {
SoundIoInStreamJackPort *isjp = &isj->ports[ch];
if (!(isj->areas[ch].ptr = (char*)jack_port_get_buffer(isjp->dest_port, *frame_count)))
return SoundIoErrorStreaming;
isj->areas[ch].step = instream->bytes_per_sample;
}
*out_frame_count = isj->frames_left;
*out_areas = isj->areas; *out_areas = isj->areas;
return 0; return 0;
} }
static int instream_end_read_jack(struct SoundIoPrivate *si, struct SoundIoInStreamPrivate *is) { static int instream_end_read_jack(struct SoundIoPrivate *si, struct SoundIoInStreamPrivate *is) {
SoundIoInStreamJack *isj = &is->backend_data.jack;
isj->frames_left = 0;
return 0; return 0;
} }

View file

@ -50,7 +50,6 @@ struct SoundIoOutStreamJack {
int frames_left; int frames_left;
SoundIoOutStreamJackPort ports[SOUNDIO_MAX_CHANNELS]; SoundIoOutStreamJackPort ports[SOUNDIO_MAX_CHANNELS];
SoundIoChannelArea areas[SOUNDIO_MAX_CHANNELS]; SoundIoChannelArea areas[SOUNDIO_MAX_CHANNELS];
char *buf_ptrs[SOUNDIO_MAX_CHANNELS];
}; };
struct SoundIoInStreamJackPort { struct SoundIoInStreamJackPort {
@ -62,8 +61,10 @@ struct SoundIoInStreamJackPort {
struct SoundIoInStreamJack { struct SoundIoInStreamJack {
jack_client_t *client; jack_client_t *client;
int period_size; int period_size;
int frames_left;
SoundIoInStreamJackPort ports[SOUNDIO_MAX_CHANNELS]; SoundIoInStreamJackPort ports[SOUNDIO_MAX_CHANNELS];
SoundIoChannelArea areas[SOUNDIO_MAX_CHANNELS]; SoundIoChannelArea areas[SOUNDIO_MAX_CHANNELS];
char *buf_ptrs[SOUNDIO_MAX_CHANNELS];
}; };
#endif #endif

View file

@ -641,8 +641,11 @@ static void playback_stream_underflow_callback(pa_stream *stream, void *userdata
} }
static void playback_stream_write_callback(pa_stream *stream, size_t nbytes, void *userdata) { static void playback_stream_write_callback(pa_stream *stream, size_t nbytes, void *userdata) {
SoundIoOutStream *outstream = (SoundIoOutStream*)(userdata); SoundIoOutStreamPrivate *os = (SoundIoOutStreamPrivate*)(userdata);
int frame_count = ((int)nbytes) / outstream->bytes_per_frame; SoundIoOutStreamPulseAudio *ospa = &os->backend_data.pulseaudio;
SoundIoOutStream *outstream = &os->pub;
ospa->bytes_left = nbytes;
int frame_count = ospa->bytes_left / outstream->bytes_per_frame;
outstream->write_callback(outstream, frame_count); outstream->write_callback(outstream, frame_count);
} }
@ -750,17 +753,23 @@ static int outstream_start_pa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os) {
} }
static int outstream_begin_write_pa(SoundIoPrivate *si, static int outstream_begin_write_pa(SoundIoPrivate *si,
SoundIoOutStreamPrivate *os, SoundIoChannelArea **out_areas, int *frame_count) SoundIoOutStreamPrivate *os, SoundIoChannelArea **out_areas, int *out_frame_count)
{ {
*out_areas = nullptr;
SoundIoOutStream *outstream = &os->pub; SoundIoOutStream *outstream = &os->pub;
SoundIoOutStreamPulseAudio *ospa = &os->backend_data.pulseaudio; SoundIoOutStreamPulseAudio *ospa = &os->backend_data.pulseaudio;
pa_stream *stream = ospa->stream; pa_stream *stream = ospa->stream;
size_t byte_count = *frame_count * outstream->bytes_per_frame; if (ospa->bytes_left <= 0) {
*out_frame_count = 0;
*out_areas = nullptr;
return 0;
}
if (pa_stream_begin_write(stream, (void**)&ospa->write_ptr, &byte_count)) // PulseAudio docs recommend setting this to (size_t) -1 but I have found
// that this causes a too small buffer to be returned.
ospa->byte_count = ospa->bytes_left;
if (pa_stream_begin_write(stream, (void**)&ospa->write_ptr, &ospa->byte_count))
return SoundIoErrorStreaming; return SoundIoErrorStreaming;
for (int ch = 0; ch < outstream->layout.channel_count; ch += 1) { for (int ch = 0; ch < outstream->layout.channel_count; ch += 1) {
@ -768,21 +777,18 @@ static int outstream_begin_write_pa(SoundIoPrivate *si,
ospa->areas[ch].step = outstream->bytes_per_frame; ospa->areas[ch].step = outstream->bytes_per_frame;
} }
*frame_count = byte_count / outstream->bytes_per_frame; *out_frame_count = ospa->byte_count / outstream->bytes_per_frame;
*out_areas = ospa->areas; *out_areas = ospa->areas;
return 0; return 0;
} }
static int outstream_end_write_pa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os, int frame_count) { static int outstream_end_write_pa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os) {
SoundIoOutStream *outstream = &os->pub;
SoundIoOutStreamPulseAudio *ospa = &os->backend_data.pulseaudio; SoundIoOutStreamPulseAudio *ospa = &os->backend_data.pulseaudio;
pa_stream *stream = ospa->stream; pa_stream *stream = ospa->stream;
assert(frame_count >= 0); if (pa_stream_write(stream, ospa->write_ptr, ospa->byte_count, nullptr, 0, PA_SEEK_RELATIVE))
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 SoundIoErrorStreaming;
ospa->bytes_left -= ospa->byte_count;
return 0; return 0;
} }
@ -844,9 +850,11 @@ 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;
SoundIoInStreamPulseAudio *ispa = &is->backend_data.pulseaudio;
assert(nbytes % instream->bytes_per_frame == 0); assert(nbytes % instream->bytes_per_frame == 0);
assert(nbytes > 0); assert(nbytes > 0);
int available_frame_count = nbytes / instream->bytes_per_frame; ispa->bytes_left = nbytes;
int available_frame_count = ispa->bytes_left / instream->bytes_per_frame;
instream->read_callback(instream, available_frame_count); instream->read_callback(instream, available_frame_count);
} }
@ -900,6 +908,7 @@ static int instream_open_pa(SoundIoPrivate *si, SoundIoInStreamPrivate *is) {
pa_stream_set_state_callback(stream, recording_stream_state_callback, is); pa_stream_set_state_callback(stream, recording_stream_state_callback, is);
pa_stream_set_read_callback(stream, recording_stream_read_callback, is); pa_stream_set_read_callback(stream, recording_stream_read_callback, is);
// TODO handle overflow callback
ispa->buffer_attr.maxlength = UINT32_MAX; ispa->buffer_attr.maxlength = UINT32_MAX;
ispa->buffer_attr.tlength = UINT32_MAX; ispa->buffer_attr.tlength = UINT32_MAX;
@ -955,7 +964,7 @@ static int instream_start_pa(SoundIoPrivate *si, SoundIoInStreamPrivate *is) {
} }
static int instream_begin_read_pa(SoundIoPrivate *si, static int instream_begin_read_pa(SoundIoPrivate *si,
SoundIoInStreamPrivate *is, SoundIoChannelArea **out_areas, int *frame_count) SoundIoInStreamPrivate *is, SoundIoChannelArea **out_areas, int *out_frame_count)
{ {
SoundIoInStream *instream = &is->pub; SoundIoInStream *instream = &is->pub;
SoundIoInStreamPulseAudio *ispa = &is->backend_data.pulseaudio; SoundIoInStreamPulseAudio *ispa = &is->backend_data.pulseaudio;
@ -963,24 +972,30 @@ static int instream_begin_read_pa(SoundIoPrivate *si,
assert(ispa->stream_ready); assert(ispa->stream_ready);
char *data; if (ispa->bytes_left <= 0) {
size_t nbytes = *frame_count * instream->bytes_per_frame; *out_frame_count = 0;
if (pa_stream_peek(stream, (const void **)&data, &nbytes)) {
*out_areas = nullptr; *out_areas = nullptr;
*frame_count = 0; return 0;
}
char *data;
ispa->byte_count = (size_t)-1;
if (pa_stream_peek(stream, (const void **)&data, &ispa->byte_count)) {
*out_areas = nullptr;
*out_frame_count = 0;
return SoundIoErrorStreaming; return SoundIoErrorStreaming;
} }
*out_frame_count = ispa->byte_count / instream->bytes_per_frame;
if (data) { if (data) {
for (int ch = 0; ch < instream->layout.channel_count; ch += 1) { for (int ch = 0; ch < instream->layout.channel_count; ch += 1) {
ispa->areas[ch].ptr = data + instream->bytes_per_sample * ch; ispa->areas[ch].ptr = data + instream->bytes_per_sample * ch;
ispa->areas[ch].step = instream->bytes_per_frame; ispa->areas[ch].step = instream->bytes_per_frame;
} }
*frame_count = nbytes / instream->bytes_per_frame;
*out_areas = ispa->areas; *out_areas = ispa->areas;
} else { } else {
*frame_count = nbytes / instream->bytes_per_frame;
*out_areas = nullptr; *out_areas = nullptr;
} }
@ -992,7 +1007,7 @@ static int instream_end_read_pa(SoundIoPrivate *si, SoundIoInStreamPrivate *is)
pa_stream *stream = ispa->stream; pa_stream *stream = ispa->stream;
if (pa_stream_drop(stream)) if (pa_stream_drop(stream))
return SoundIoErrorStreaming; return SoundIoErrorStreaming;
ispa->bytes_left -= ispa->byte_count;
return 0; return 0;
} }

View file

@ -15,9 +15,7 @@
int soundio_pulseaudio_init(struct SoundIoPrivate *si); int soundio_pulseaudio_init(struct SoundIoPrivate *si);
struct SoundIoDevicePulseAudio { struct SoundIoDevicePulseAudio { };
};
struct SoundIoPulseAudio { struct SoundIoPulseAudio {
int connection_err; int connection_err;
@ -50,6 +48,8 @@ struct SoundIoOutStreamPulseAudio {
atomic_bool stream_ready; atomic_bool stream_ready;
pa_buffer_attr buffer_attr; pa_buffer_attr buffer_attr;
char *write_ptr; char *write_ptr;
size_t byte_count;
int bytes_left;
SoundIoChannelArea areas[SOUNDIO_MAX_CHANNELS]; SoundIoChannelArea areas[SOUNDIO_MAX_CHANNELS];
}; };
@ -57,6 +57,8 @@ 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;
size_t byte_count;
int bytes_left;
SoundIoChannelArea areas[SOUNDIO_MAX_CHANNELS]; SoundIoChannelArea areas[SOUNDIO_MAX_CHANNELS];
}; };

View file

@ -352,15 +352,14 @@ int soundio_outstream_begin_write(struct SoundIoOutStream *outstream,
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;
assert(*frame_count > 0);
return si->outstream_begin_write(si, os, areas, frame_count); return si->outstream_begin_write(si, os, areas, frame_count);
} }
int soundio_outstream_end_write(struct SoundIoOutStream *outstream, int frame_count) { int soundio_outstream_end_write(struct SoundIoOutStream *outstream) {
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_end_write(si, os, frame_count); return si->outstream_end_write(si, os);
} }
static void default_outstream_error_callback(struct SoundIoOutStream *os, int err) { static void default_outstream_error_callback(struct SoundIoOutStream *os, int err) {

View file

@ -126,8 +126,8 @@ struct SoundIoPrivate {
void (*outstream_destroy)(struct SoundIoPrivate *, struct SoundIoOutStreamPrivate *); void (*outstream_destroy)(struct SoundIoPrivate *, struct SoundIoOutStreamPrivate *);
int (*outstream_start)(struct SoundIoPrivate *, struct SoundIoOutStreamPrivate *); int (*outstream_start)(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 *out_frame_count);
int (*outstream_end_write)(struct SoundIoPrivate *, struct SoundIoOutStreamPrivate *, int frame_count); int (*outstream_end_write)(struct SoundIoPrivate *, struct SoundIoOutStreamPrivate *);
int (*outstream_clear_buffer)(struct SoundIoPrivate *, struct SoundIoOutStreamPrivate *); int (*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);
@ -136,7 +136,7 @@ struct SoundIoPrivate {
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 *);
int (*instream_begin_read)(struct SoundIoPrivate *, struct SoundIoInStreamPrivate *, int (*instream_begin_read)(struct SoundIoPrivate *, struct SoundIoInStreamPrivate *,
SoundIoChannelArea **out_areas, int *frame_count); SoundIoChannelArea **out_areas, int *out_frame_count);
int (*instream_end_read)(struct SoundIoPrivate *, struct SoundIoInStreamPrivate *); int (*instream_end_read)(struct SoundIoPrivate *, struct SoundIoInStreamPrivate *);
int (*instream_pause)(struct SoundIoPrivate *, struct SoundIoInStreamPrivate *, bool pause); int (*instream_pause)(struct SoundIoPrivate *, struct SoundIoInStreamPrivate *, bool pause);

View file

@ -41,6 +41,8 @@ static float seconds_end = 9.0f;
static void write_callback(struct SoundIoOutStream *outstream, int requested_frame_count) { static void write_callback(struct SoundIoOutStream *outstream, int requested_frame_count) {
float float_sample_rate = outstream->sample_rate; float float_sample_rate = outstream->sample_rate;
float seconds_per_frame = 1.0f / float_sample_rate; float seconds_per_frame = 1.0f / float_sample_rate;
struct SoundIoChannelArea *areas;
int frame_count;
int err; int err;
if (!caused_underflow && seconds_offset >= 3.0f) { if (!caused_underflow && seconds_offset >= 3.0f) {
@ -54,9 +56,6 @@ static void write_callback(struct SoundIoOutStream *outstream, int requested_fra
} }
for (;;) { for (;;) {
int frame_count = requested_frame_count;
struct SoundIoChannelArea *areas;
if ((err = soundio_outstream_begin_write(outstream, &areas, &frame_count))) if ((err = soundio_outstream_begin_write(outstream, &areas, &frame_count)))
panic("%s", soundio_strerror(err)); panic("%s", soundio_strerror(err));
@ -76,15 +75,11 @@ 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_end_write(outstream, frame_count))) { if ((err = soundio_outstream_end_write(outstream))) {
if (err == SoundIoErrorUnderflow) if (err == SoundIoErrorUnderflow)
return; return;
panic("%s", soundio_strerror(err)); panic("%s", soundio_strerror(err));
} }
requested_frame_count -= frame_count;
if (requested_frame_count <= 0)
break;
} }
} }
@ -124,6 +119,8 @@ int main(int argc, char **argv) {
if (err) if (err)
panic("error connecting: %s", soundio_strerror(err)); panic("error connecting: %s", soundio_strerror(err));
soundio_flush_events(soundio);
int default_out_device_index = soundio_default_output_device_index(soundio); int default_out_device_index = soundio_default_output_device_index(soundio);
if (default_out_device_index < 0) if (default_out_device_index < 0)
panic("no output device found"); panic("no output device found");