#include #include #include #include void xas_audio_copy(xas_audio_format format, void *dest, void *src, size_t index_dest, size_t index_src, size_t count) { size_t stride = format.channels * format.sample_size; memcpy(((uint8_t *)dest) + stride * index_dest, ((uint8_t *)src) + stride * index_src, stride * count); } void xas_audio_zero(xas_audio_format format, void *dest, size_t index, size_t count) { size_t stride = format.channels * format.sample_size; memset((uint8_t *)dest + stride * index, '\0', stride * count); } static xas_audio_stream *stream_new(enum xas_audio_stream_type type, void *callback, xas_audio_cleanup cleanup, xas_audio_format format, size_t buffer_size, void *ctx) { xas_audio_stream *stream; size_t total = sizeof(xas_audio_stream) + format.channels * format.sample_size * buffer_size; if ((stream = malloc(total)) == NULL) { goto error_malloc_stream; } stream->type = type; stream->format.channels = format.channels; stream->format.sample_size = format.sample_size; stream->format.sample_rate = format.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, xas_audio_format format, size_t buffer_size, void *ctx) { return stream_new(XAS_AUDIO_STREAM_SINK, drain, cleanup, format, buffer_size, ctx); } xas_audio_stream *xas_audio_stream_new_source(xas_audio_fill fill, xas_audio_cleanup cleanup, xas_audio_format format, size_t buffer_size, void *ctx) { return stream_new(XAS_AUDIO_STREAM_SOURCE, fill, cleanup, format, buffer_size, ctx); } 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->format.channels * stream->format.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; xas_audio_copy(stream->format, stream + 1, samples, stream->buffer_count, index_i, remaining); if (stream->drain(stream->ctx, stream + 1, 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. */ xas_audio_copy(stream->format, stream + 1, samples, stream->buffer_count, index_i, remaining); 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; }