#include #include #include #include static xas_audio_stream *stream_new(enum xas_audio_stream_type type, void *callback, xas_audio_cleanup cleanup, void *ctx, size_t channels, size_t sample_size, size_t sample_rate, size_t buffer_size) { xas_audio_stream *stream; size_t total = sizeof(xas_audio_stream) + channels * sample_size * buffer_size; if ((stream = malloc(total)) == NULL) { goto error_malloc_stream; } stream->type = type; stream->channels = channels; stream->sample_size = sample_size; stream->sample_rate = sample_rate; stream->buffer_size = buffer_size; stream->buffer_count = 0; stream->callback = callback; stream->cleanup = cleanup; stream->ctx = ctx; return stream; error_malloc_stream: return NULL; } xas_audio_stream *xas_audio_stream_new_sink(xas_audio_drain drain, xas_audio_cleanup cleanup, void *ctx, size_t channels, size_t sample_size, size_t sample_rate, size_t buffer_size) { return stream_new(XAS_AUDIO_STREAM_SINK, drain, cleanup, ctx, channels, sample_size, sample_rate, buffer_size); } xas_audio_stream *xas_audio_stream_new_source(xas_audio_fill fill, xas_audio_cleanup cleanup, void *ctx, size_t channels, size_t sample_size, size_t sample_rate, size_t buffer_size) { return stream_new(XAS_AUDIO_STREAM_SOURCE, fill, cleanup, ctx, channels, sample_size, sample_rate, buffer_size); } void xas_audio_stream_destroy(xas_audio_stream *stream) { if (stream->cleanup) { stream->cleanup(stream->ctx, stream); } free(stream); } static inline int stream_flush(xas_audio_stream *stream) { if (stream->buffer_count == 0) { return 0; } if (stream->drain(stream->ctx, stream + 1, stream->buffer_count, stream) < 0) { goto error_stream_drain; } stream->buffer_count = 0; return 0; error_stream_drain: return -1; } static inline void *ptr(xas_audio_stream *stream, void *buf, size_t index) { return ((uint8_t *)buf) + stream->channels * stream->sample_size * index; } ssize_t xas_audio_stream_write(xas_audio_stream *stream, void *samples, size_t count) { size_t index_i = 0; if (stream->buffer_count + count > stream->buffer_size) { /* * If the number of samples offered, plus the number of items currently * in the buffer exceeds the buffer size, fill the buffer to capacity * and flush it. */ size_t remaining = stream->buffer_size - stream->buffer_count; memcpy(ptr(stream, stream + 1, stream->buffer_count), ptr(stream, samples, index_i), remaining * stream->channels * stream->sample_size); if (stream->drain(stream->ctx, ptr(stream, stream + 1, 0), stream->buffer_size, stream) < 0) { goto error_stream_drain; } index_i += remaining; stream->buffer_count = 0; } /* * While there are still samples to buffer or flush... */ while (index_i < count) { size_t remaining = count - index_i; if (remaining >= stream->buffer_size) { /* * If the number of samples remaining is greater than the target * buffer size, then drain directly from the source buffer to the * output an amount equal to the buffer size. */ if (stream->drain(stream->ctx, ptr(stream, samples, index_i), stream->buffer_size, stream) < 0) { goto error_stream_drain; } index_i += stream->buffer_size; } else { /* * Enough of the input has been drained that it can be copied to * the target buffer. */ memcpy(ptr(stream, stream + 1, stream->buffer_count), ptr(stream, samples, index_i), remaining * stream->channels * stream->sample_size); index_i += remaining; stream->buffer_count += remaining; } } return index_i; error_stream_drain: return -1; } ssize_t xas_audio_stream_read(xas_audio_stream *stream, void **samples, size_t count) { *samples = stream + 1; if (count > stream->buffer_size) { count = stream->buffer_size; } return stream->fill(stream->ctx, *samples, count, stream); } int xas_audio_stream_flush(xas_audio_stream *stream) { if (stream->type == XAS_AUDIO_STREAM_SINK) { return stream_flush(stream); } errno = EINVAL; return -1; }