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/)
- [PulseAudio](http://www.freedesktop.org/wiki/Software/PulseAudio/)
- [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)
- (planned) [WASAPI](https://msdn.microsoft.com/en-us/library/windows/desktop/dd371455%28v=vs.85%29.aspx)
- (planned) [ASIO](http://www.asio4all.com/)
@ -242,6 +242,9 @@ view `coverage/index.html` in a browser.
## Roadmap
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
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
@ -251,6 +254,7 @@ view `coverage/index.html` in a browser.
should do the same.
0. implement WASAPI (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. 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.
@ -275,7 +279,7 @@ view `coverage/index.html` in a browser.
0. -fvisibility=hidden and then explicitly export stuff, or
explicitly make the unexported stuff private
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. Expose JACK options in `jack_client_open`
0. custom allocator support

View file

@ -51,26 +51,24 @@ static void panic(const char *format, ...) {
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) {
struct SoundIoChannelArea *areas;
int frame_count;
int err;
char *write_ptr = soundio_ring_buffer_write_ptr(ring_buffer);
int free_bytes = soundio_ring_buffer_free_count(ring_buffer);
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 (;;) {
int frame_count = frames_left;
struct SoundIoChannelArea *areas;
if ((err = soundio_instream_begin_read(instream, &areas, &frame_count)))
panic("begin read error: %s", soundio_strerror(err));
if (frame_count <= 0)
if (!frame_count)
break;
if (!areas) {
@ -90,36 +88,42 @@ static void read_callback(struct SoundIoInStream *instream, int available_frame_
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_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);
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) {
int err;
struct SoundIoChannelArea *areas;
int frame_count;
int err;
char *read_ptr = soundio_ring_buffer_read_ptr(ring_buffer);
int fill_bytes = soundio_ring_buffer_fill_count(ring_buffer);
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 (;;) {
int frame_count = frames_left;
if (frame_count <= 0)
break;
if ((err = soundio_outstream_begin_write(outstream, &areas, &frame_count)))
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));
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) {

View file

@ -35,11 +35,11 @@ static float seconds_offset = 0.0f;
static void write_callback(struct SoundIoOutStream *outstream, int requested_frame_count) {
float float_sample_rate = outstream->sample_rate;
float seconds_per_frame = 1.0f / float_sample_rate;
int frame_count;
int err;
for (;;) {
int frame_count = requested_frame_count;
for (;;) {
struct SoundIoChannelArea *areas;
if ((err = soundio_outstream_begin_write(outstream, &areas, &frame_count)))
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;
if ((err = soundio_outstream_end_write(outstream, frame_count))) {
if ((err = soundio_outstream_end_write(outstream))) {
if (err == SoundIoErrorUnderflow)
return;
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.
void *userdata;
// In this callback, you call `soundio_outstream_begin_write` and
// `soundio_outstream_end_write`. `requested_frame_count` will always be
// greater than 0.
// `soundio_outstream_end_write` as many times as necessary to write
// 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);
// This optional callback happens when the sound device runs out of buffered
// 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.
// * `areas` - (out) The memory addresses you can write data to. It is OK to
// modify the pointers if that helps you iterate.
// * `frame_count` - (in/out) Provide the number of frames you want to write.
// Returned will be the number of frames you actually can write. Must be
// greater than 0 frames.
// * `frame_count` - (out) Returns the number of frames you actually can write.
// 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 sio_sine.c for an example.
// 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,
struct SoundIoChannelArea **areas, int *frame_count);
// Commits the write that you began with `soundio_outstream_begin_write`.
// You must call this function only from the `write_callback` thread context.
// 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.
// You must call this function only from the `write_callback` thread context.
@ -760,12 +762,11 @@ 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. 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.
// to modify the pointers if that helps you iterate. There might 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` - (out) - Returns 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 sio_microphone.c for an example.

View file

@ -1013,6 +1013,7 @@ void outstream_thread_run(void *arg) {
continue;
}
osa->frames_left = avail;
outstream->write_callback(outstream, avail);
continue;
}
@ -1080,6 +1081,7 @@ static void instream_thread_run(void *arg) {
continue;
}
isa->frames_left = avail;
instream->read_callback(instream, avail);
continue;
}
@ -1283,7 +1285,7 @@ static int outstream_start_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;
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;
}
*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) {
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].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 {
const snd_pcm_channel_area_t *areas;
snd_pcm_uframes_t frames = *frame_count;
snd_pcm_uframes_t frames = osa->frames_left;
int err;
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);
}
*frame_count = frames;
osa->frames_to_write = frames;
*out_frame_count = osa->frames_to_write;
}
*out_areas = osa->areas;
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;
SoundIoOutStream *outstream = &os->pub;
snd_pcm_sframes_t commitres;
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) {
char *ptrs[SOUNDIO_MAX_CHANNELS];
for (int ch = 0; ch < outstream->layout.channel_count; ch += 1) {
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 {
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;
if (err == -EPIPE || err == -ESTRPIPE)
return SoundIoErrorUnderflow;
else
return SoundIoErrorStreaming;
}
osa->frames_left -= osa->frames_to_write;
assert(osa->frames_left >= 0);
return 0;
}
@ -1560,7 +1567,7 @@ static int instream_start_alsa(SoundIoPrivate *si, SoundIoInStreamPrivate *is) {
}
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;
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->read_frame_count = min(*frame_count, isa->period_size);
*frame_count = isa->read_frame_count;
isa->read_frame_count = min(isa->frames_left, isa->period_size);
*out_frame_count = 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;
if ((err = instream_xrun_recovery(is, err)) < 0)
return SoundIoErrorStreaming;
@ -1588,17 +1596,18 @@ static int instream_begin_read_alsa(SoundIoPrivate *si,
ptrs[ch] = isa->areas[ch].ptr;
}
isa->read_frame_count = min(*frame_count, isa->period_size);
*frame_count = isa->read_frame_count;
isa->read_frame_count = min(isa->frames_left, isa->period_size);
*out_frame_count = 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;
if ((err = instream_xrun_recovery(is, err)) < 0)
return SoundIoErrorStreaming;
}
} else {
const snd_pcm_channel_area_t *areas;
snd_pcm_uframes_t frames = *frame_count;
snd_pcm_uframes_t frames = isa->frames_left;
int err;
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;
*frame_count = isa->read_frame_count;
*out_frame_count = isa->read_frame_count;
}
*out_areas = isa->areas;
@ -1626,9 +1635,9 @@ static int instream_end_read_alsa(SoundIoPrivate *si, SoundIoInStreamPrivate *is
SoundIoInStreamAlsa *isa = &is->backend_data.alsa;
if (isa->access == SND_PCM_ACCESS_RW_INTERLEAVED) {
return 0;
// nothing to do
} else if (isa->access == SND_PCM_ACCESS_RW_NONINTERLEAVED) {
return 0;
// nothing to do
} else {
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) {
@ -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;
}

View file

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

View file

@ -865,8 +865,7 @@ static int outstream_begin_write_ca(struct SoundIoPrivate *si, struct SoundIoOut
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;
osca->buffer_index += 1;
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 free_bytes = soundio_ring_buffer_capacity(&osd->ring_buffer) - fill_bytes;
int free_frames = free_bytes / outstream->bytes_per_frame;
osd->frames_left = free_frames;
outstream->write_callback(outstream, free_frames);
double start_time = soundio_os_get_time();
long frames_consumed = 0;
@ -47,10 +48,12 @@ static void playback_thread_run(void *arg) {
if (frames_to_kill > fill_frames) {
outstream->underflow_callback(outstream);
osd->frames_left = free_frames;
outstream->write_callback(outstream, free_frames);
frames_consumed = 0;
start_time = soundio_os_get_time();
} else if (free_frames > 0) {
osd->frames_left = 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();
}
if (fill_frames > 0) {
isd->frames_left = 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,
SoundIoOutStreamPrivate *os, SoundIoChannelArea **out_areas, int *frame_count)
SoundIoOutStreamPrivate *os, SoundIoChannelArea **out_areas, int *out_frame_count)
{
*out_areas = nullptr;
SoundIoOutStream *outstream = &os->pub;
SoundIoOutStreamDummy *osd = &os->backend_data.dummy;
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;
*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;
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_frame_count = osd->frames_left;
*out_areas = osd->areas;
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;
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);
osd->frames_left = 0;
return 0;
}
@ -310,37 +305,29 @@ static int instream_start_dummy(SoundIoPrivate *si, SoundIoInStreamPrivate *is)
}
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;
SoundIoInStreamDummy *isd = &is->backend_data.dummy;
assert(*frame_count >= 0);
assert(*frame_count <= isd->buffer_frame_count);
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;
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_frame_count = isd->frames_left;
*out_areas = isd->areas;
return 0;
}
static int instream_end_read_dummy(SoundIoPrivate *si, SoundIoInStreamPrivate *is) {
SoundIoInStreamDummy *isd = &is->backend_data.dummy;
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);
isd->frames_left = 0;
return 0;
}

View file

@ -30,6 +30,7 @@ struct SoundIoOutStreamDummy {
struct SoundIoOsCond *cond;
atomic_flag abort_flag;
int buffer_frame_count;
int frames_left;
struct SoundIoRingBuffer ring_buffer;
double playback_start_time;
SoundIoChannelArea areas[SOUNDIO_MAX_CHANNELS];
@ -39,7 +40,7 @@ struct SoundIoInStreamDummy {
struct SoundIoOsThread *thread;
struct SoundIoOsCond *cond;
atomic_flag abort_flag;
int read_frame_count;
int frames_left;
int buffer_frame_count;
struct SoundIoRingBuffer ring_buffer;
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;
for (int ch = 0; ch < outstream->layout.channel_count; ch += 1) {
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);
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,
SoundIoChannelArea **out_areas, int *frame_count)
SoundIoChannelArea **out_areas, int *out_frame_count)
{
SoundIoOutStream *outstream = &os->pub;
SoundIoOutStreamJack *osj = &os->backend_data.jack;
*frame_count = min(*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_frame_count = osj->frames_left;
*out_areas = osj->areas;
return 0;
}
static int outstream_end_write_jack(struct SoundIoPrivate *si, struct SoundIoOutStreamPrivate *os,
int frame_count)
{
SoundIoOutStream *outstream = &os->pub;
static int outstream_end_write_jack(struct SoundIoPrivate *si, struct SoundIoOutStreamPrivate *os) {
SoundIoOutStreamJack *osj = &os->backend_data.jack;
assert(frame_count <= osj->frames_left);
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;
}
}
osj->frames_left = 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) {
SoundIoInStreamPrivate *is = (SoundIoInStreamPrivate *)arg;
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;
}
@ -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,
SoundIoChannelArea **out_areas, int *frame_count)
SoundIoChannelArea **out_areas, int *out_frame_count)
{
SoundIoInStream *instream = &is->pub;
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;
return 0;
}
static int instream_end_read_jack(struct SoundIoPrivate *si, struct SoundIoInStreamPrivate *is) {
SoundIoInStreamJack *isj = &is->backend_data.jack;
isj->frames_left = 0;
return 0;
}

View file

@ -50,7 +50,6 @@ struct SoundIoOutStreamJack {
int frames_left;
SoundIoOutStreamJackPort ports[SOUNDIO_MAX_CHANNELS];
SoundIoChannelArea areas[SOUNDIO_MAX_CHANNELS];
char *buf_ptrs[SOUNDIO_MAX_CHANNELS];
};
struct SoundIoInStreamJackPort {
@ -62,8 +61,10 @@ struct SoundIoInStreamJackPort {
struct SoundIoInStreamJack {
jack_client_t *client;
int period_size;
int frames_left;
SoundIoInStreamJackPort ports[SOUNDIO_MAX_CHANNELS];
SoundIoChannelArea areas[SOUNDIO_MAX_CHANNELS];
char *buf_ptrs[SOUNDIO_MAX_CHANNELS];
};
#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) {
SoundIoOutStream *outstream = (SoundIoOutStream*)(userdata);
int frame_count = ((int)nbytes) / outstream->bytes_per_frame;
SoundIoOutStreamPrivate *os = (SoundIoOutStreamPrivate*)(userdata);
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);
}
@ -750,17 +753,23 @@ static int outstream_start_pa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os) {
}
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;
SoundIoOutStreamPulseAudio *ospa = &os->backend_data.pulseaudio;
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;
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;
}
*frame_count = byte_count / outstream->bytes_per_frame;
*out_frame_count = ospa->byte_count / outstream->bytes_per_frame;
*out_areas = ospa->areas;
return 0;
}
static int outstream_end_write_pa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os, int frame_count) {
SoundIoOutStream *outstream = &os->pub;
static int outstream_end_write_pa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os) {
SoundIoOutStreamPulseAudio *ospa = &os->backend_data.pulseaudio;
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))
if (pa_stream_write(stream, ospa->write_ptr, ospa->byte_count, nullptr, 0, PA_SEEK_RELATIVE))
return SoundIoErrorStreaming;
ospa->bytes_left -= ospa->byte_count;
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) {
SoundIoInStreamPrivate *is = (SoundIoInStreamPrivate*)userdata;
SoundIoInStream *instream = &is->pub;
SoundIoInStreamPulseAudio *ispa = &is->backend_data.pulseaudio;
assert(nbytes % instream->bytes_per_frame == 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);
}
@ -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_read_callback(stream, recording_stream_read_callback, is);
// TODO handle overflow callback
ispa->buffer_attr.maxlength = 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,
SoundIoInStreamPrivate *is, SoundIoChannelArea **out_areas, int *frame_count)
SoundIoInStreamPrivate *is, SoundIoChannelArea **out_areas, int *out_frame_count)
{
SoundIoInStream *instream = &is->pub;
SoundIoInStreamPulseAudio *ispa = &is->backend_data.pulseaudio;
@ -963,24 +972,30 @@ static int instream_begin_read_pa(SoundIoPrivate *si,
assert(ispa->stream_ready);
char *data;
size_t nbytes = *frame_count * instream->bytes_per_frame;
if (pa_stream_peek(stream, (const void **)&data, &nbytes)) {
if (ispa->bytes_left <= 0) {
*out_frame_count = 0;
*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;
}
*out_frame_count = ispa->byte_count / instream->bytes_per_frame;
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;
}
@ -992,7 +1007,7 @@ static int instream_end_read_pa(SoundIoPrivate *si, SoundIoInStreamPrivate *is)
pa_stream *stream = ispa->stream;
if (pa_stream_drop(stream))
return SoundIoErrorStreaming;
ispa->bytes_left -= ispa->byte_count;
return 0;
}

View file

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

View file

@ -352,15 +352,14 @@ int soundio_outstream_begin_write(struct SoundIoOutStream *outstream,
SoundIo *soundio = outstream->device->soundio;
SoundIoPrivate *si = (SoundIoPrivate *)soundio;
SoundIoOutStreamPrivate *os = (SoundIoOutStreamPrivate *)outstream;
assert(*frame_count > 0);
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;
SoundIoPrivate *si = (SoundIoPrivate *)soundio;
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) {

View file

@ -126,8 +126,8 @@ struct SoundIoPrivate {
void (*outstream_destroy)(struct SoundIoPrivate *, struct SoundIoOutStreamPrivate *);
int (*outstream_start)(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);
SoundIoChannelArea **out_areas, int *out_frame_count);
int (*outstream_end_write)(struct SoundIoPrivate *, struct SoundIoOutStreamPrivate *);
int (*outstream_clear_buffer)(struct SoundIoPrivate *, struct SoundIoOutStreamPrivate *);
int (*outstream_pause)(struct SoundIoPrivate *, struct SoundIoOutStreamPrivate *, bool pause);
@ -136,7 +136,7 @@ struct SoundIoPrivate {
void (*instream_destroy)(struct SoundIoPrivate *, struct SoundIoInStreamPrivate *);
int (*instream_start)(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_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) {
float float_sample_rate = outstream->sample_rate;
float seconds_per_frame = 1.0f / float_sample_rate;
struct SoundIoChannelArea *areas;
int frame_count;
int err;
if (!caused_underflow && seconds_offset >= 3.0f) {
@ -54,9 +56,6 @@ static void write_callback(struct SoundIoOutStream *outstream, int requested_fra
}
for (;;) {
int frame_count = requested_frame_count;
struct SoundIoChannelArea *areas;
if ((err = soundio_outstream_begin_write(outstream, &areas, &frame_count)))
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;
if ((err = soundio_outstream_end_write(outstream, frame_count))) {
if ((err = soundio_outstream_end_write(outstream))) {
if (err == SoundIoErrorUnderflow)
return;
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)
panic("error connecting: %s", soundio_strerror(err));
soundio_flush_events(soundio);
int default_out_device_index = soundio_default_output_device_index(soundio);
if (default_out_device_index < 0)
panic("no output device found");