mirror of
https://github.com/Ryujinx/libsoundio.git
synced 2024-12-22 18:25:35 +00:00
callbacks supply min and max frame count parameters
This changes the semantics of the callbacks so that instead of a single `requested_frame_count` or `available_frame_count`, the callbacks get a minimum frame count and maximum frame count. The callback must write at least the minimum or get an underflow. The minimum will be 0 on ALSA, PulseAudio, and Dummy, and will equal the maximum on CoreAudio and JACK. This ensures optimal behavior regardless of buffer size.
This commit is contained in:
parent
8da5ae0798
commit
c381526205
|
@ -27,6 +27,10 @@ behavior on every platform.
|
|||
- 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/)
|
||||
* Supports optimal usage of each supported backend. The same API does the
|
||||
right thing whether the backend has a fixed buffer size, such as on JACK and
|
||||
CoreAudio, or whether it allows directly managing the buffer, such as on
|
||||
ALSA or PulseAudio.
|
||||
* C library. Depends only on the respective backend API libraries and libc.
|
||||
Does *not* depend on libstdc++, and does *not* have exceptions, run-time type
|
||||
information, or [setjmp](http://latentcontent.net/2007/12/05/libpng-worst-api-ever/).
|
||||
|
|
|
@ -15,6 +15,8 @@
|
|||
|
||||
static const double microphone_latency = 0.2; // seconds
|
||||
|
||||
struct SoundIoRingBuffer *ring_buffer = NULL;
|
||||
|
||||
static enum SoundIoFormat prioritized_formats[] = {
|
||||
SoundIoFormatFloat32NE,
|
||||
SoundIoFormatFloat32FE,
|
||||
|
@ -37,6 +39,7 @@ static enum SoundIoFormat prioritized_formats[] = {
|
|||
SoundIoFormatInvalid,
|
||||
};
|
||||
|
||||
|
||||
__attribute__ ((cold))
|
||||
__attribute__ ((noreturn))
|
||||
__attribute__ ((format (printf, 1, 2)))
|
||||
|
@ -49,22 +52,26 @@ static void panic(const char *format, ...) {
|
|||
abort();
|
||||
}
|
||||
|
||||
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 frame_count_min, int frame_count_max) {
|
||||
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;
|
||||
|
||||
//fprintf(stderr, "read_callback %d free %d\n", available_frame_count, free_count);
|
||||
|
||||
if (available_frame_count > free_count)
|
||||
if (frame_count_min > free_count)
|
||||
panic("ring buffer overflow");
|
||||
|
||||
int write_frames = min_int(free_count, frame_count_max);
|
||||
int frames_left = write_frames;
|
||||
|
||||
for (;;) {
|
||||
int frame_count = frames_left;
|
||||
|
||||
if ((err = soundio_instream_begin_read(instream, &areas, &frame_count)))
|
||||
panic("begin read error: %s", soundio_strerror(err));
|
||||
|
||||
|
@ -88,13 +95,17 @@ 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 = available_frame_count * instream->bytes_per_frame;
|
||||
int advance_bytes = write_frames * instream->bytes_per_frame;
|
||||
soundio_ring_buffer_advance_write_ptr(ring_buffer, advance_bytes);
|
||||
}
|
||||
|
||||
static void write_callback(struct SoundIoOutStream *outstream, int requested_frame_count) {
|
||||
static void write_callback(struct SoundIoOutStream *outstream, int frame_count_min, int frame_count_max) {
|
||||
struct SoundIoChannelArea *areas;
|
||||
int frame_count;
|
||||
int err;
|
||||
|
@ -103,9 +114,7 @@ static void write_callback(struct SoundIoOutStream *outstream, int requested_fra
|
|||
int fill_bytes = soundio_ring_buffer_fill_count(ring_buffer);
|
||||
int fill_count = fill_bytes / outstream->bytes_per_frame;
|
||||
|
||||
//fprintf(stderr, "write_callback %d fill %d\n", requested_frame_count, fill_count);
|
||||
|
||||
if (requested_frame_count > fill_count) {
|
||||
if (frame_count_min > fill_count) {
|
||||
// Ring buffer does not have enough data, fill with zeroes.
|
||||
for (;;) {
|
||||
if ((err = soundio_outstream_begin_write(outstream, &areas, &frame_count)))
|
||||
|
@ -123,7 +132,12 @@ static void write_callback(struct SoundIoOutStream *outstream, int requested_fra
|
|||
}
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
int read_count = min_int(frame_count_max, fill_count);
|
||||
int frames_left = read_count;
|
||||
|
||||
while (frames_left > 0) {
|
||||
int frame_count = frames_left;
|
||||
|
||||
if ((err = soundio_outstream_begin_write(outstream, &areas, &frame_count)))
|
||||
panic("begin write error: %s", soundio_strerror(err));
|
||||
|
||||
|
@ -140,9 +154,11 @@ static void write_callback(struct SoundIoOutStream *outstream, int requested_fra
|
|||
|
||||
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, requested_frame_count * outstream->bytes_per_frame);
|
||||
soundio_ring_buffer_advance_read_ptr(ring_buffer, read_count * outstream->bytes_per_frame);
|
||||
}
|
||||
|
||||
static void underflow_callback(struct SoundIoOutStream *outstream) {
|
||||
|
|
|
@ -32,15 +32,16 @@ static int usage(char *exe) {
|
|||
|
||||
static const float PI = 3.1415926535f;
|
||||
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 frame_count_min, int frame_count_max) {
|
||||
float float_sample_rate = outstream->sample_rate;
|
||||
float seconds_per_frame = 1.0f / float_sample_rate;
|
||||
int frame_count;
|
||||
struct SoundIoChannelArea *areas;
|
||||
int err;
|
||||
|
||||
int frames_left = frame_count_max;
|
||||
|
||||
for (;;) {
|
||||
struct SoundIoChannelArea *areas;
|
||||
int frame_count = frames_left;
|
||||
if ((err = soundio_outstream_begin_write(outstream, &areas, &frame_count)))
|
||||
panic("%s", soundio_strerror(err));
|
||||
|
||||
|
@ -65,6 +66,10 @@ static void write_callback(struct SoundIoOutStream *outstream, int requested_fra
|
|||
return;
|
||||
panic("%s", soundio_strerror(err));
|
||||
}
|
||||
|
||||
frames_left -= frame_count;
|
||||
if (frames_left <= 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -419,11 +419,15 @@ struct SoundIoOutStream {
|
|||
void *userdata;
|
||||
// In this callback, you call `soundio_outstream_begin_write` and
|
||||
// `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);
|
||||
// at minimum `frame_count_min` frames and at maximum `frame_count_max`
|
||||
// frames. `frame_count_max` will always be greater than 0. Note that you
|
||||
// should write as many frames as you can; `frame_count_min` might be 0 and
|
||||
// you can still get a buffer underflow if you always write
|
||||
// `frame_count_min` frames. See `sio_sine.c` for example.
|
||||
// For Dummy, ALSA, and PulseAudio, `frame_count_min` will be 0. For JACK
|
||||
// and CoreAudio `frame_count_min` will be equal to `frame_count_max`.
|
||||
void (*write_callback)(struct SoundIoOutStream *,
|
||||
int frame_count_min, int frame_count_max);
|
||||
// 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.
|
||||
|
@ -500,8 +504,12 @@ 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);
|
||||
// `soundio_instream_end_read` as many times as necessary to read at
|
||||
// minimum `frame_count_min` frames and at maximum `frame_count_max`
|
||||
// frames. If you return from `read_callback` without having read
|
||||
// `frame_count_min`, the frames will be dropped. `frame_count_max` is how
|
||||
// many frames are available to read.
|
||||
void (*read_callback)(struct SoundIoInStream *, int frame_count_min, int frame_count_max);
|
||||
// Optional callback. `err` is always SoundIoErrorStreaming.
|
||||
// SoundIoErrorStreaming is an unrecoverable error. The stream is in an
|
||||
// invalid state and must be destroyed.
|
||||
|
@ -718,13 +726,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` - (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.
|
||||
// * `frame_count` - (in/out) Provide the number of frames you want to write.
|
||||
// Returned will be the number of frames you can actually write, which is
|
||||
// also the number of frames that will be written when you call
|
||||
// `soundio_outstream_end_write`. The value returned will always be less
|
||||
// than or equal to the value provided.
|
||||
// It is your responsibility to call this function exactly as many times as
|
||||
// necessary to meet the `frame_count_min` and `frame_count_max` criteria from
|
||||
// `write_callback`. See `sio_sine.c` for 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`.
|
||||
// If you call this function with `frame_count` less than the `frame_count_min`
|
||||
// parameter from `write_callback` it returns SoundIoErrorInvalid.
|
||||
// If this function returns an error, do not call `soundio_outstream_end_write`.
|
||||
int soundio_outstream_begin_write(struct SoundIoOutStream *outstream,
|
||||
struct SoundIoChannelArea **areas, int *frame_count);
|
||||
|
||||
|
@ -768,11 +783,15 @@ int soundio_instream_start(struct SoundIoInStream *instream);
|
|||
// 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.
|
||||
// * `frame_count` - (in/out) - Provide the number of frames you want to read;
|
||||
// returns the number of frames you can actually read. The returned value
|
||||
// will always be less than or equal to the provided value. If the provided
|
||||
// value is less than `frame_count_min` from `read_callback` this function
|
||||
// returns with SoundIoErrorInvalid.
|
||||
// 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.
|
||||
// correct number of times according to the `frame_count_min` and
|
||||
// `frame_count_max` criteria from `read_callback`. See sio_microphone.c for an
|
||||
// example.
|
||||
// You must call this function only from the `read_callback` thread context.
|
||||
// After calling this function, read data from `areas` and then use
|
||||
// `soundio_instream_end_read` to actually remove the data from the buffer
|
||||
|
|
49
src/alsa.cpp
49
src/alsa.cpp
|
@ -1011,8 +1011,7 @@ void outstream_thread_run(void *arg) {
|
|||
continue;
|
||||
}
|
||||
|
||||
osa->frames_left = avail;
|
||||
outstream->write_callback(outstream, avail);
|
||||
outstream->write_callback(outstream, 0, avail);
|
||||
continue;
|
||||
}
|
||||
case SND_PCM_STATE_XRUN:
|
||||
|
@ -1079,8 +1078,7 @@ static void instream_thread_run(void *arg) {
|
|||
continue;
|
||||
}
|
||||
|
||||
isa->frames_left = avail;
|
||||
instream->read_callback(instream, avail);
|
||||
instream->read_callback(instream, 0, avail);
|
||||
continue;
|
||||
}
|
||||
case SND_PCM_STATE_XRUN:
|
||||
|
@ -1283,7 +1281,7 @@ static int outstream_start_alsa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os)
|
|||
}
|
||||
|
||||
int outstream_begin_write_alsa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os,
|
||||
struct SoundIoChannelArea **out_areas, int *out_frame_count)
|
||||
struct SoundIoChannelArea **out_areas, int *frame_count)
|
||||
{
|
||||
*out_areas = nullptr;
|
||||
SoundIoOutStreamAlsa *osa = &os->backend_data.alsa;
|
||||
|
@ -1295,19 +1293,19 @@ int outstream_begin_write_alsa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os,
|
|||
osa->areas[ch].step = outstream->bytes_per_frame;
|
||||
}
|
||||
|
||||
osa->frames_to_write = min(osa->frames_left, osa->period_size);
|
||||
*out_frame_count = osa->frames_to_write;
|
||||
osa->write_frame_count = min(*frame_count, osa->period_size);
|
||||
*frame_count = osa->write_frame_count;
|
||||
} 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;
|
||||
}
|
||||
|
||||
osa->frames_to_write = min(osa->frames_left, osa->period_size);
|
||||
*out_frame_count = osa->frames_to_write;
|
||||
osa->write_frame_count = min(*frame_count, osa->period_size);
|
||||
*frame_count = osa->write_frame_count;
|
||||
} else {
|
||||
const snd_pcm_channel_area_t *areas;
|
||||
snd_pcm_uframes_t frames = osa->frames_left;
|
||||
snd_pcm_uframes_t frames = *frame_count;
|
||||
int err;
|
||||
|
||||
if ((err = snd_pcm_mmap_begin(osa->handle, &areas, &osa->offset, &frames)) < 0) {
|
||||
|
@ -1325,8 +1323,8 @@ int outstream_begin_write_alsa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os,
|
|||
(osa->areas[ch].step * osa->offset);
|
||||
}
|
||||
|
||||
osa->frames_to_write = frames;
|
||||
*out_frame_count = osa->frames_to_write;
|
||||
osa->write_frame_count = frames;
|
||||
*frame_count = osa->write_frame_count;
|
||||
}
|
||||
|
||||
*out_areas = osa->areas;
|
||||
|
@ -1339,27 +1337,24 @@ static int outstream_end_write_alsa(SoundIoPrivate *si, SoundIoOutStreamPrivate
|
|||
|
||||
snd_pcm_sframes_t commitres;
|
||||
if (osa->access == SND_PCM_ACCESS_RW_INTERLEAVED) {
|
||||
commitres = snd_pcm_writei(osa->handle, osa->sample_buffer, osa->frames_to_write);
|
||||
commitres = snd_pcm_writei(osa->handle, osa->sample_buffer, osa->write_frame_count);
|
||||
} 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, osa->frames_to_write);
|
||||
commitres = snd_pcm_writen(osa->handle, (void**)ptrs, osa->write_frame_count);
|
||||
} else {
|
||||
commitres = snd_pcm_mmap_commit(osa->handle, osa->offset, osa->frames_to_write);
|
||||
commitres = snd_pcm_mmap_commit(osa->handle, osa->offset, osa->write_frame_count);
|
||||
}
|
||||
|
||||
if (commitres < 0 || commitres != osa->frames_to_write) {
|
||||
if (commitres < 0 || commitres != osa->write_frame_count) {
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -1565,7 +1560,7 @@ static int instream_start_alsa(SoundIoPrivate *si, SoundIoInStreamPrivate *is) {
|
|||
}
|
||||
|
||||
static int instream_begin_read_alsa(SoundIoPrivate *si,
|
||||
SoundIoInStreamPrivate *is, SoundIoChannelArea **out_areas, int *out_frame_count)
|
||||
SoundIoInStreamPrivate *is, SoundIoChannelArea **out_areas, int *frame_count)
|
||||
{
|
||||
*out_areas = nullptr;
|
||||
SoundIoInStreamAlsa *isa = &is->backend_data.alsa;
|
||||
|
@ -1577,8 +1572,8 @@ static int instream_begin_read_alsa(SoundIoPrivate *si,
|
|||
isa->areas[ch].step = instream->bytes_per_frame;
|
||||
}
|
||||
|
||||
isa->read_frame_count = min(isa->frames_left, isa->period_size);
|
||||
*out_frame_count = isa->read_frame_count;
|
||||
isa->read_frame_count = min(*frame_count, isa->period_size);
|
||||
*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 != isa->read_frame_count) {
|
||||
|
@ -1594,8 +1589,8 @@ static int instream_begin_read_alsa(SoundIoPrivate *si,
|
|||
ptrs[ch] = isa->areas[ch].ptr;
|
||||
}
|
||||
|
||||
isa->read_frame_count = min(isa->frames_left, isa->period_size);
|
||||
*out_frame_count = isa->read_frame_count;
|
||||
isa->read_frame_count = min(*frame_count, isa->period_size);
|
||||
*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 != isa->read_frame_count) {
|
||||
|
@ -1605,7 +1600,7 @@ static int instream_begin_read_alsa(SoundIoPrivate *si,
|
|||
}
|
||||
} else {
|
||||
const snd_pcm_channel_area_t *areas;
|
||||
snd_pcm_uframes_t frames = isa->frames_left;
|
||||
snd_pcm_uframes_t frames = *frame_count;
|
||||
int err;
|
||||
|
||||
if ((err = snd_pcm_mmap_begin(isa->handle, &areas, &isa->offset, &frames)) < 0) {
|
||||
|
@ -1622,7 +1617,7 @@ static int instream_begin_read_alsa(SoundIoPrivate *si,
|
|||
}
|
||||
|
||||
isa->read_frame_count = frames;
|
||||
*out_frame_count = isa->read_frame_count;
|
||||
*frame_count = isa->read_frame_count;
|
||||
}
|
||||
|
||||
*out_areas = isa->areas;
|
||||
|
@ -1645,8 +1640,6 @@ static int instream_end_read_alsa(SoundIoPrivate *si, SoundIoInStreamPrivate *is
|
|||
}
|
||||
}
|
||||
|
||||
isa->frames_left -= isa->read_frame_count;
|
||||
assert(isa->frames_left >= 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -49,8 +49,7 @@ struct SoundIoOutStreamAlsa {
|
|||
SoundIoOsThread *thread;
|
||||
atomic_flag thread_exit_flag;
|
||||
int period_size;
|
||||
int frames_left;
|
||||
int frames_to_write;
|
||||
int write_frame_count;
|
||||
SoundIoChannelArea areas[SOUNDIO_MAX_CHANNELS];
|
||||
};
|
||||
|
||||
|
@ -67,7 +66,6 @@ struct SoundIoInStreamAlsa {
|
|||
SoundIoOsThread *thread;
|
||||
atomic_flag thread_exit_flag;
|
||||
int period_size;
|
||||
int frames_left;
|
||||
int read_frame_count;
|
||||
SoundIoChannelArea areas[SOUNDIO_MAX_CHANNELS];
|
||||
};
|
||||
|
|
|
@ -857,7 +857,8 @@ static OSStatus write_callback_ca(void *userdata, AudioUnitRenderActionFlags *io
|
|||
|
||||
osca->io_data = io_data;
|
||||
osca->buffer_index = 0;
|
||||
outstream->write_callback(outstream, in_number_frames);
|
||||
osca->frames_left = osca->callback_frames;
|
||||
outstream->write_callback(outstream, osca->frames_left, osca->frames_left);
|
||||
osca->io_data = nullptr;
|
||||
|
||||
return noErr;
|
||||
|
@ -951,31 +952,35 @@ static int outstream_start_ca(struct SoundIoPrivate *si, struct SoundIoOutStream
|
|||
}
|
||||
|
||||
static int outstream_begin_write_ca(struct SoundIoPrivate *si, struct SoundIoOutStreamPrivate *os,
|
||||
SoundIoChannelArea **out_areas, int *out_frame_count)
|
||||
SoundIoChannelArea **out_areas, int *frame_count)
|
||||
{
|
||||
SoundIoOutStream *outstream = &os->pub;
|
||||
SoundIoOutStreamCoreAudio *osca = &os->backend_data.coreaudio;
|
||||
|
||||
if (osca->buffer_index < osca->io_data->mNumberBuffers) {
|
||||
AudioBuffer *audio_buffer = &osca->io_data->mBuffers[osca->buffer_index];
|
||||
assert(audio_buffer->mNumberChannels == outstream->layout.channel_count);
|
||||
*out_frame_count = audio_buffer->mDataByteSize / outstream->bytes_per_frame;
|
||||
assert((audio_buffer->mDataByteSize % outstream->bytes_per_frame) == 0);
|
||||
for (int ch = 0; ch < outstream->layout.channel_count; ch += 1) {
|
||||
osca->areas[ch].ptr = ((char*)audio_buffer->mData) + outstream->bytes_per_sample * ch;
|
||||
osca->areas[ch].step = outstream->bytes_per_frame;
|
||||
}
|
||||
*out_areas = osca->areas;
|
||||
} else {
|
||||
*out_areas = nullptr;
|
||||
*out_frame_count = 0;
|
||||
if (osca->buffer_index >= ocsa->io_data->mNumberBuffers)
|
||||
return SoundIoErrorInvalid;
|
||||
|
||||
if (*frame_count != osca->frames_left)
|
||||
return SoundIoErrorInvalid;
|
||||
|
||||
AudioBuffer *audio_buffer = &osca->io_data->mBuffers[osca->buffer_index];
|
||||
assert(audio_buffer->mNumberChannels == outstream->layout.channel_count);
|
||||
osca->write_frame_count = audio_buffer->mDataByteSize / outstream->bytes_per_frame;
|
||||
*frame_count = osca->write_frame_count;
|
||||
assert((audio_buffer->mDataByteSize % outstream->bytes_per_frame) == 0);
|
||||
for (int ch = 0; ch < outstream->layout.channel_count; ch += 1) {
|
||||
osca->areas[ch].ptr = ((char*)audio_buffer->mData) + outstream->bytes_per_sample * ch;
|
||||
osca->areas[ch].step = outstream->bytes_per_frame;
|
||||
}
|
||||
*out_areas = osca->areas;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int outstream_end_write_ca(struct SoundIoPrivate *si, struct SoundIoOutStreamPrivate *os) {
|
||||
SoundIoOutStreamCoreAudio *osca = &os->backend_data.coreaudio;
|
||||
osca->buffer_index += 1;
|
||||
osca->frames_left -= osca->write_frame_count;
|
||||
assert(osca->frames_left >= 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -45,6 +45,8 @@ struct SoundIoOutStreamCoreAudio {
|
|||
AudioComponentInstance output_instance;
|
||||
AudioBufferList *io_data;
|
||||
int buffer_index;
|
||||
int frames_left;
|
||||
int write_frame_count;
|
||||
SoundIoChannelArea areas[SOUNDIO_MAX_CHANNELS];
|
||||
};
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ static void playback_thread_run(void *arg) {
|
|||
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);
|
||||
outstream->write_callback(outstream, 0, free_frames);
|
||||
double start_time = soundio_os_get_time();
|
||||
long frames_consumed = 0;
|
||||
|
||||
|
@ -49,12 +49,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);
|
||||
outstream->write_callback(outstream, 0, 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);
|
||||
outstream->write_callback(outstream, 0, free_frames);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -94,7 +94,7 @@ static void capture_thread_run(void *arg) {
|
|||
}
|
||||
if (fill_frames > 0) {
|
||||
isd->frames_left = fill_frames;
|
||||
instream->read_callback(instream, fill_frames);
|
||||
instream->read_callback(instream, 0, fill_frames);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -201,18 +201,20 @@ static int outstream_start_dummy(SoundIoPrivate *si, SoundIoOutStreamPrivate *os
|
|||
}
|
||||
|
||||
static int outstream_begin_write_dummy(SoundIoPrivate *si,
|
||||
SoundIoOutStreamPrivate *os, SoundIoChannelArea **out_areas, int *out_frame_count)
|
||||
SoundIoOutStreamPrivate *os, SoundIoChannelArea **out_areas, int *frame_count)
|
||||
{
|
||||
SoundIoOutStream *outstream = &os->pub;
|
||||
SoundIoOutStreamDummy *osd = &os->backend_data.dummy;
|
||||
|
||||
assert(*frame_count <= osd->frames_left);
|
||||
|
||||
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;
|
||||
osd->write_frame_count = *frame_count;
|
||||
*out_areas = osd->areas;
|
||||
return 0;
|
||||
}
|
||||
|
@ -220,9 +222,9 @@ static int outstream_begin_write_dummy(SoundIoPrivate *si,
|
|||
static int outstream_end_write_dummy(SoundIoPrivate *si, SoundIoOutStreamPrivate *os) {
|
||||
SoundIoOutStreamDummy *osd = &os->backend_data.dummy;
|
||||
SoundIoOutStream *outstream = &os->pub;
|
||||
int byte_count = osd->frames_left * outstream->bytes_per_frame;
|
||||
int byte_count = osd->write_frame_count * outstream->bytes_per_frame;
|
||||
soundio_ring_buffer_advance_write_ptr(&osd->ring_buffer, byte_count);
|
||||
osd->frames_left = 0;
|
||||
osd->frames_left -= osd->write_frame_count;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -305,18 +307,20 @@ static int instream_start_dummy(SoundIoPrivate *si, SoundIoInStreamPrivate *is)
|
|||
}
|
||||
|
||||
static int instream_begin_read_dummy(SoundIoPrivate *si,
|
||||
SoundIoInStreamPrivate *is, SoundIoChannelArea **out_areas, int *out_frame_count)
|
||||
SoundIoInStreamPrivate *is, SoundIoChannelArea **out_areas, int *frame_count)
|
||||
{
|
||||
SoundIoInStream *instream = &is->pub;
|
||||
SoundIoInStreamDummy *isd = &is->backend_data.dummy;
|
||||
|
||||
assert(*frame_count <= isd->frames_left);
|
||||
|
||||
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;
|
||||
isd->read_frame_count = *frame_count;
|
||||
*out_areas = isd->areas;
|
||||
|
||||
return 0;
|
||||
|
@ -325,9 +329,9 @@ static int instream_begin_read_dummy(SoundIoPrivate *si,
|
|||
static int instream_end_read_dummy(SoundIoPrivate *si, SoundIoInStreamPrivate *is) {
|
||||
SoundIoInStreamDummy *isd = &is->backend_data.dummy;
|
||||
SoundIoInStream *instream = &is->pub;
|
||||
int byte_count = isd->frames_left * instream->bytes_per_frame;
|
||||
int byte_count = isd->read_frame_count * instream->bytes_per_frame;
|
||||
soundio_ring_buffer_advance_read_ptr(&isd->ring_buffer, byte_count);
|
||||
isd->frames_left = 0;
|
||||
isd->frames_left -= isd->read_frame_count;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -31,6 +31,7 @@ struct SoundIoOutStreamDummy {
|
|||
atomic_flag abort_flag;
|
||||
int buffer_frame_count;
|
||||
int frames_left;
|
||||
int write_frame_count;
|
||||
struct SoundIoRingBuffer ring_buffer;
|
||||
double playback_start_time;
|
||||
SoundIoChannelArea areas[SOUNDIO_MAX_CHANNELS];
|
||||
|
@ -41,6 +42,7 @@ struct SoundIoInStreamDummy {
|
|||
struct SoundIoOsCond *cond;
|
||||
atomic_flag abort_flag;
|
||||
int frames_left;
|
||||
int read_frame_count;
|
||||
int buffer_frame_count;
|
||||
struct SoundIoRingBuffer ring_buffer;
|
||||
SoundIoChannelArea areas[SOUNDIO_MAX_CHANNELS];
|
||||
|
|
16
src/jack.cpp
16
src/jack.cpp
|
@ -345,7 +345,7 @@ static int outstream_process_callback(jack_nframes_t nframes, void *arg) {
|
|||
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, osj->frames_left);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -526,11 +526,13 @@ 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 *out_frame_count)
|
||||
SoundIoChannelArea **out_areas, int *frame_count)
|
||||
{
|
||||
SoundIoOutStreamJack *osj = &os->backend_data.jack;
|
||||
|
||||
*out_frame_count = osj->frames_left;
|
||||
if (*frame_count != osj->frames_left)
|
||||
return SoundIoErrorInvalid;
|
||||
|
||||
*out_areas = osj->areas;
|
||||
|
||||
return 0;
|
||||
|
@ -601,7 +603,7 @@ static int instream_process_callback(jack_nframes_t nframes, void *arg) {
|
|||
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);
|
||||
instream->read_callback(instream, isj->frames_left, isj->frames_left);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -726,11 +728,13 @@ 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 *out_frame_count)
|
||||
SoundIoChannelArea **out_areas, int *frame_count)
|
||||
{
|
||||
SoundIoInStreamJack *isj = &is->backend_data.jack;
|
||||
|
||||
*out_frame_count = isj->frames_left;
|
||||
if (*frame_count != isj->frames_left)
|
||||
return SoundIoErrorInvalid;
|
||||
|
||||
*out_areas = isj->areas;
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -642,11 +642,9 @@ 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) {
|
||||
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);
|
||||
int frame_count = nbytes / outstream->bytes_per_frame;
|
||||
outstream->write_callback(outstream, 0, frame_count);
|
||||
}
|
||||
|
||||
static void outstream_destroy_pa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os) {
|
||||
|
@ -746,9 +744,9 @@ static int outstream_start_pa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os) {
|
|||
|
||||
pa_threaded_mainloop_lock(sipa->main_loop);
|
||||
|
||||
ospa->bytes_left = pa_stream_writable_size(ospa->stream);
|
||||
int frame_count = ospa->bytes_left / outstream->bytes_per_frame;
|
||||
outstream->write_callback(outstream, frame_count);
|
||||
ospa->write_byte_count = pa_stream_writable_size(ospa->stream);
|
||||
int frame_count = ospa->write_byte_count / outstream->bytes_per_frame;
|
||||
outstream->write_callback(outstream, 0, frame_count);
|
||||
|
||||
pa_operation *op = pa_stream_cork(ospa->stream, false, nullptr, nullptr);
|
||||
if (!op) {
|
||||
|
@ -765,23 +763,14 @@ static int outstream_start_pa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os) {
|
|||
}
|
||||
|
||||
static int outstream_begin_write_pa(SoundIoPrivate *si,
|
||||
SoundIoOutStreamPrivate *os, SoundIoChannelArea **out_areas, int *out_frame_count)
|
||||
SoundIoOutStreamPrivate *os, SoundIoChannelArea **out_areas, int *frame_count)
|
||||
{
|
||||
SoundIoOutStream *outstream = &os->pub;
|
||||
SoundIoOutStreamPulseAudio *ospa = &os->backend_data.pulseaudio;
|
||||
pa_stream *stream = ospa->stream;
|
||||
|
||||
if (ospa->bytes_left <= 0) {
|
||||
*out_frame_count = 0;
|
||||
*out_areas = nullptr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 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))
|
||||
ospa->write_byte_count = *frame_count * outstream->bytes_per_frame;
|
||||
if (pa_stream_begin_write(stream, (void**)&ospa->write_ptr, &ospa->write_byte_count))
|
||||
return SoundIoErrorStreaming;
|
||||
|
||||
for (int ch = 0; ch < outstream->layout.channel_count; ch += 1) {
|
||||
|
@ -789,7 +778,7 @@ static int outstream_begin_write_pa(SoundIoPrivate *si,
|
|||
ospa->areas[ch].step = outstream->bytes_per_frame;
|
||||
}
|
||||
|
||||
*out_frame_count = ospa->byte_count / outstream->bytes_per_frame;
|
||||
*frame_count = ospa->write_byte_count / outstream->bytes_per_frame;
|
||||
*out_areas = ospa->areas;
|
||||
|
||||
return 0;
|
||||
|
@ -798,9 +787,8 @@ static int outstream_begin_write_pa(SoundIoPrivate *si,
|
|||
static int outstream_end_write_pa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os) {
|
||||
SoundIoOutStreamPulseAudio *ospa = &os->backend_data.pulseaudio;
|
||||
pa_stream *stream = ospa->stream;
|
||||
if (pa_stream_write(stream, ospa->write_ptr, ospa->byte_count, nullptr, 0, PA_SEEK_RELATIVE))
|
||||
if (pa_stream_write(stream, ospa->write_ptr, ospa->write_byte_count, nullptr, 0, PA_SEEK_RELATIVE))
|
||||
return SoundIoErrorStreaming;
|
||||
ospa->bytes_left -= ospa->byte_count;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -866,12 +854,10 @@ static void recording_stream_state_callback(pa_stream *stream, void *userdata) {
|
|||
static void recording_stream_read_callback(pa_stream *stream, size_t nbytes, void *userdata) {
|
||||
SoundIoInStreamPrivate *is = (SoundIoInStreamPrivate*)userdata;
|
||||
SoundIoInStream *instream = &is->pub;
|
||||
SoundIoInStreamPulseAudio *ispa = &is->backend_data.pulseaudio;
|
||||
assert(nbytes % instream->bytes_per_frame == 0);
|
||||
assert(nbytes > 0);
|
||||
ispa->bytes_left = nbytes;
|
||||
int available_frame_count = ispa->bytes_left / instream->bytes_per_frame;
|
||||
instream->read_callback(instream, available_frame_count);
|
||||
int available_frame_count = nbytes / instream->bytes_per_frame;
|
||||
instream->read_callback(instream, 0, available_frame_count);
|
||||
}
|
||||
|
||||
static void instream_destroy_pa(SoundIoPrivate *si, SoundIoInStreamPrivate *is) {
|
||||
|
@ -980,7 +966,7 @@ static int instream_start_pa(SoundIoPrivate *si, SoundIoInStreamPrivate *is) {
|
|||
}
|
||||
|
||||
static int instream_begin_read_pa(SoundIoPrivate *si,
|
||||
SoundIoInStreamPrivate *is, SoundIoChannelArea **out_areas, int *out_frame_count)
|
||||
SoundIoInStreamPrivate *is, SoundIoChannelArea **out_areas, int *frame_count)
|
||||
{
|
||||
SoundIoInStream *instream = &is->pub;
|
||||
SoundIoInStreamPulseAudio *ispa = &is->backend_data.pulseaudio;
|
||||
|
@ -988,42 +974,56 @@ static int instream_begin_read_pa(SoundIoPrivate *si,
|
|||
|
||||
assert(ispa->stream_ready);
|
||||
|
||||
if (ispa->bytes_left <= 0) {
|
||||
*out_frame_count = 0;
|
||||
*out_areas = nullptr;
|
||||
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;
|
||||
if (!ispa->peek_buf) {
|
||||
if (pa_stream_peek(stream, (const void **)&ispa->peek_buf, &ispa->peek_buf_size)) {
|
||||
soundio_panic("TODO");
|
||||
}
|
||||
|
||||
*out_areas = ispa->areas;
|
||||
} else {
|
||||
*out_areas = nullptr;
|
||||
ispa->peek_buf_frames_left = ispa->peek_buf_size / instream->bytes_per_frame;
|
||||
ispa->peek_buf_index = 0;
|
||||
|
||||
// hole
|
||||
if (!ispa->peek_buf) {
|
||||
*frame_count = ispa->peek_buf_frames_left;
|
||||
*out_areas = nullptr;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
ispa->read_frame_count = min(*frame_count, ispa->peek_buf_frames_left);
|
||||
*frame_count = ispa->read_frame_count;
|
||||
for (int ch = 0; ch < instream->layout.channel_count; ch += 1) {
|
||||
ispa->areas[ch].ptr = ispa->peek_buf + ispa->peek_buf_index + instream->bytes_per_sample * ch;
|
||||
ispa->areas[ch].step = instream->bytes_per_frame;
|
||||
}
|
||||
|
||||
*out_areas = ispa->areas;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int instream_end_read_pa(SoundIoPrivate *si, SoundIoInStreamPrivate *is) {
|
||||
SoundIoInStream *instream = &is->pub;
|
||||
SoundIoInStreamPulseAudio *ispa = &is->backend_data.pulseaudio;
|
||||
pa_stream *stream = ispa->stream;
|
||||
if (pa_stream_drop(stream))
|
||||
return SoundIoErrorStreaming;
|
||||
ispa->bytes_left -= ispa->byte_count;
|
||||
|
||||
// hole
|
||||
if (!ispa->peek_buf) {
|
||||
if (pa_stream_drop(stream))
|
||||
return SoundIoErrorStreaming;
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t advance_bytes = ispa->read_frame_count * instream->bytes_per_frame;
|
||||
ispa->peek_buf_index += advance_bytes;
|
||||
ispa->peek_buf_frames_left -= ispa->read_frame_count;
|
||||
|
||||
if (ispa->peek_buf_index >= ispa->peek_buf_size) {
|
||||
if (pa_stream_drop(stream))
|
||||
return SoundIoErrorStreaming;
|
||||
ispa->peek_buf = nullptr;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -48,8 +48,7 @@ struct SoundIoOutStreamPulseAudio {
|
|||
atomic_bool stream_ready;
|
||||
pa_buffer_attr buffer_attr;
|
||||
char *write_ptr;
|
||||
size_t byte_count;
|
||||
int bytes_left;
|
||||
size_t write_byte_count;
|
||||
SoundIoChannelArea areas[SOUNDIO_MAX_CHANNELS];
|
||||
};
|
||||
|
||||
|
@ -57,8 +56,11 @@ struct SoundIoInStreamPulseAudio {
|
|||
pa_stream *stream;
|
||||
atomic_bool stream_ready;
|
||||
pa_buffer_attr buffer_attr;
|
||||
size_t byte_count;
|
||||
int bytes_left;
|
||||
char *peek_buf;
|
||||
size_t peek_buf_index;
|
||||
size_t peek_buf_size;
|
||||
int peek_buf_frames_left;
|
||||
int read_frame_count;
|
||||
SoundIoChannelArea areas[SOUNDIO_MAX_CHANNELS];
|
||||
};
|
||||
|
||||
|
|
|
@ -393,6 +393,7 @@ 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);
|
||||
}
|
||||
|
||||
|
|
|
@ -38,11 +38,10 @@ static struct SoundIoOsCond *cond = NULL;
|
|||
static struct SoundIo *soundio = NULL;
|
||||
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 frame_count_min, int frame_count_max) {
|
||||
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) {
|
||||
|
@ -55,7 +54,10 @@ static void write_callback(struct SoundIoOutStream *outstream, int requested_fra
|
|||
return;
|
||||
}
|
||||
|
||||
int frames_left = frame_count_max;
|
||||
|
||||
for (;;) {
|
||||
int frame_count = frames_left;
|
||||
if ((err = soundio_outstream_begin_write(outstream, &areas, &frame_count)))
|
||||
panic("%s", soundio_strerror(err));
|
||||
|
||||
|
@ -80,6 +82,10 @@ static void write_callback(struct SoundIoOutStream *outstream, int requested_fra
|
|||
return;
|
||||
panic("%s", soundio_strerror(err));
|
||||
}
|
||||
|
||||
frames_left -= frame_count;
|
||||
if (frames_left <= 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@ static void test_os_get_time(void) {
|
|||
}
|
||||
}
|
||||
|
||||
static void write_callback(struct SoundIoOutStream *device, int frame_count) { }
|
||||
static void write_callback(struct SoundIoOutStream *device, int frame_count_min, int frame_count_max) { }
|
||||
static void error_callback(struct SoundIoOutStream *device, int err) { }
|
||||
|
||||
static void test_create_outstream(void) {
|
||||
|
|
Loading…
Reference in a new issue